Switch to use SpannableFactory for html parsing

This commit is contained in:
Dimitry Ivanov 2018-07-21 14:08:33 +03:00
parent 9f532df752
commit bd08178a55
13 changed files with 211 additions and 91 deletions

View File

@ -31,7 +31,7 @@ public class SpannableConfiguration {
private final UrlProcessor urlProcessor;
private final SpannableHtmlParser htmlParser;
private final ImageSizeResolver imageSizeResolver;
private final SpannableFactory spannableFactory; // @since 1.1.0
private final SpannableFactory factory; // @since 1.1.0
private SpannableConfiguration(@NonNull Builder builder) {
this.theme = builder.theme;
@ -41,7 +41,7 @@ public class SpannableConfiguration {
this.urlProcessor = builder.urlProcessor;
this.htmlParser = builder.htmlParser;
this.imageSizeResolver = builder.imageSizeResolver;
this.spannableFactory = builder.spannableFactory;
this.factory = builder.factory;
}
@NonNull
@ -81,7 +81,7 @@ public class SpannableConfiguration {
@NonNull
public SpannableFactory factory() {
return spannableFactory;
return factory;
}
@SuppressWarnings("unused")
@ -95,7 +95,7 @@ public class SpannableConfiguration {
private UrlProcessor urlProcessor;
private SpannableHtmlParser htmlParser;
private ImageSizeResolver imageSizeResolver;
private SpannableFactory spannableFactory;
private SpannableFactory factory;
Builder(@NonNull Context context) {
this.context = context;
@ -150,8 +150,8 @@ public class SpannableConfiguration {
* @since 1.1.0
*/
@NonNull
public Builder spannableFactory(@NonNull SpannableFactory spannableFactory) {
this.spannableFactory = spannableFactory;
public Builder factory(@NonNull SpannableFactory factory) {
this.factory = factory;
return this;
}
@ -183,12 +183,18 @@ public class SpannableConfiguration {
}
// @since 1.1.0
if (spannableFactory == null) {
spannableFactory = SpannableFactoryDef.create();
if (factory == null) {
factory = SpannableFactoryDef.create();
}
if (htmlParser == null) {
htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver, imageSizeResolver);
htmlParser = SpannableHtmlParser.create(
factory,
theme,
asyncDrawableLoader,
urlProcessor,
linkResolver,
imageSizeResolver);
}
return new SpannableConfiguration(this);

View File

@ -70,4 +70,16 @@ public interface SpannableFactory {
@NonNull SpannableTheme theme,
@NonNull String destination,
@NonNull LinkSpan.Resolver resolver);
// Currently used by HTML parser
@Nullable
Object superScript(@NonNull SpannableTheme theme);
// Currently used by HTML parser
@Nullable
Object subScript(@NonNull SpannableTheme theme);
// Currently used by HTML parser
@Nullable
Object underline();
}

View File

@ -3,6 +3,7 @@ package ru.noties.markwon;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.style.StrikethroughSpan;
import android.text.style.UnderlineSpan;
import java.util.List;
@ -19,6 +20,8 @@ import ru.noties.markwon.spans.LinkSpan;
import ru.noties.markwon.spans.OrderedListItemSpan;
import ru.noties.markwon.spans.SpannableTheme;
import ru.noties.markwon.spans.StrongEmphasisSpan;
import ru.noties.markwon.spans.SubScriptSpan;
import ru.noties.markwon.spans.SuperScriptSpan;
import ru.noties.markwon.spans.TableRowSpan;
import ru.noties.markwon.spans.TaskListSpan;
import ru.noties.markwon.spans.ThematicBreakSpan;
@ -118,4 +121,21 @@ public class SpannableFactoryDef implements SpannableFactory {
public Object link(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull LinkSpan.Resolver resolver) {
return new LinkSpan(theme, destination, resolver);
}
@Nullable
@Override
public Object superScript(@NonNull SpannableTheme theme) {
return new SuperScriptSpan(theme);
}
@Override
public Object subScript(@NonNull SpannableTheme theme) {
return new SubScriptSpan(theme);
}
@Nullable
@Override
public Object underline() {
return new UnderlineSpan();
}
}

View File

@ -4,7 +4,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.StrikethroughSpan;
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.ext.gfm.tables.TableBody;
@ -44,20 +43,8 @@ import ru.noties.markwon.SpannableBuilder;
import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.renderer.html.SpannableHtmlParser;
import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.AsyncDrawableSpan;
import ru.noties.markwon.spans.BlockQuoteSpan;
import ru.noties.markwon.spans.BulletListItemSpan;
import ru.noties.markwon.spans.CodeSpan;
import ru.noties.markwon.spans.EmphasisSpan;
import ru.noties.markwon.spans.HeadingSpan;
import ru.noties.markwon.spans.LinkSpan;
import ru.noties.markwon.spans.OrderedListItemSpan;
import ru.noties.markwon.spans.SpannableTheme;
import ru.noties.markwon.spans.StrongEmphasisSpan;
import ru.noties.markwon.spans.TableRowSpan;
import ru.noties.markwon.spans.TaskListSpan;
import ru.noties.markwon.spans.ThematicBreakSpan;
import ru.noties.markwon.tasklist.TaskListBlock;
import ru.noties.markwon.tasklist.TaskListItem;
@ -467,9 +454,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
if (htmlInlineItems.size() > 0) {
final HtmlInlineItem item = htmlInlineItems.pop();
final Object span = htmlParser.getSpanForTag(item.tag);
if (span != null) {
setSpan(item.start, span);
}
setSpan(item.start, span);
}
} else {
@ -501,7 +486,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
final int length = builder.length();
if (span.getClass().isArray()) {
for (Object o: ((Object[]) span)) {
for (Object o : ((Object[]) span)) {
builder.setSpan(o, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {

View File

@ -2,12 +2,21 @@ package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import ru.noties.markwon.spans.StrongEmphasisSpan;
import ru.noties.markwon.SpannableFactory;
class BoldProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
/**
* @since 1.1.0
*/
BoldProvider(@NonNull SpannableFactory factory) {
this.factory = factory;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new StrongEmphasisSpan();
return factory.strongEmphasis();
}
}

View File

@ -10,26 +10,29 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.UrlProcessor;
import ru.noties.markwon.renderer.ImageSize;
import ru.noties.markwon.renderer.ImageSizeResolver;
import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.AsyncDrawableSpan;
import ru.noties.markwon.spans.SpannableTheme;
class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
private final SpannableFactory factory;
private final SpannableTheme theme;
private final AsyncDrawable.Loader loader;
private final UrlProcessor urlProcessor;
private final ImageSizeResolver imageSizeResolver;
ImageProviderImpl(
@NonNull SpannableFactory factory,
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor,
@NonNull ImageSizeResolver imageSizeResolver
) {
this.factory = factory;
this.theme = theme;
this.loader = loader;
this.urlProcessor = urlProcessor;
@ -56,11 +59,26 @@ class ImageProviderImpl implements SpannableHtmlParser.ImageProvider {
replacement = "\uFFFC";
}
final AsyncDrawable drawable = new AsyncDrawable(destination, loader, imageSizeResolver, parseImageSize(attributes));
final AsyncDrawableSpan span = new AsyncDrawableSpan(theme, drawable);
final Object span = factory.image(
theme,
destination,
loader,
imageSizeResolver,
parseImageSize(attributes),
false);
final SpannableString string = new SpannableString(replacement);
string.setSpan(span, 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (span != null) {
final int length = string.length();
if (span.getClass().isArray()) {
for (Object o : ((Object[]) span)) {
string.setSpan(o, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
string.setSpan(span, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
spanned = string;
} else {

View File

@ -2,12 +2,21 @@ package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import ru.noties.markwon.spans.EmphasisSpan;
import ru.noties.markwon.SpannableFactory;
class ItalicsProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
/**
* @since 1.1.0
*/
ItalicsProvider(@NonNull SpannableFactory factory) {
this.factory = factory;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new EmphasisSpan();
return factory.emphasis();
}
}

View File

@ -5,20 +5,24 @@ import android.text.TextUtils;
import java.util.Map;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.UrlProcessor;
import ru.noties.markwon.spans.LinkSpan;
import ru.noties.markwon.spans.SpannableTheme;
class LinkProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
private final SpannableTheme theme;
private final UrlProcessor urlProcessor;
private final LinkSpan.Resolver resolver;
LinkProvider(
@NonNull SpannableFactory factory,
@NonNull SpannableTheme theme,
@NonNull UrlProcessor urlProcessor,
@NonNull LinkSpan.Resolver resolver) {
this.factory = factory;
this.theme = theme;
this.urlProcessor = urlProcessor;
this.resolver = resolver;
@ -34,7 +38,7 @@ class LinkProvider implements SpannableHtmlParser.SpanProvider {
if (!TextUtils.isEmpty(href)) {
final String destination = urlProcessor.process(href);
span = new LinkSpan(theme, destination, resolver);
span = factory.link(theme, destination, resolver);
} else {
span = null;

View File

@ -11,6 +11,7 @@ import java.util.HashMap;
import java.util.Map;
import ru.noties.markwon.LinkResolverDef;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.UrlProcessor;
import ru.noties.markwon.UrlProcessorNoOp;
import ru.noties.markwon.renderer.ImageSizeResolver;
@ -22,53 +23,68 @@ import ru.noties.markwon.spans.SpannableTheme;
@SuppressWarnings("WeakerAccess")
public class SpannableHtmlParser {
// creates default parser
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader
) {
return builderWithDefaults(theme, loader, null, null, null)
.build();
}
// // creates default parser
// @NonNull
// public static SpannableHtmlParser create(
// @NonNull SpannableTheme theme,
// @NonNull AsyncDrawable.Loader loader
// ) {
// return builderWithDefaults(theme, loader, null, null, null)
// .build();
// }
//
// /**
// * @since 1.0.1
// */
// @NonNull
// public static SpannableHtmlParser create(
// @NonNull SpannableTheme theme,
// @NonNull AsyncDrawable.Loader loader,
// @NonNull ImageSizeResolver imageSizeResolver
// ) {
// return builderWithDefaults(theme, loader, null, null, imageSizeResolver)
// .build();
// }
//
// @NonNull
// public static SpannableHtmlParser create(
// @NonNull SpannableTheme theme,
// @NonNull AsyncDrawable.Loader loader,
// @NonNull UrlProcessor urlProcessor,
// @NonNull LinkSpan.Resolver resolver
// ) {
// return builderWithDefaults(theme, loader, urlProcessor, resolver, null)
// .build();
// }
// /**
// * @since 1.0.1
// */
// @NonNull
// public static SpannableHtmlParser create(
// @NonNull SpannableTheme theme,
// @NonNull AsyncDrawable.Loader loader,
// @NonNull UrlProcessor urlProcessor,
// @NonNull LinkSpan.Resolver resolver,
// @NonNull ImageSizeResolver imageSizeResolver
// ) {
// return builderWithDefaults(theme, loader, urlProcessor, resolver, imageSizeResolver)
// .build();
// }
/**
* @since 1.0.1
*/
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull ImageSizeResolver imageSizeResolver
) {
return builderWithDefaults(theme, loader, null, null, imageSizeResolver)
.build();
}
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor,
@NonNull LinkSpan.Resolver resolver
) {
return builderWithDefaults(theme, loader, urlProcessor, resolver, null)
.build();
}
/**
* @since 1.0.1
* @since 1.1.0
*/
@NonNull
public static SpannableHtmlParser create(
@NonNull SpannableFactory factory,
@NonNull SpannableTheme theme,
@NonNull AsyncDrawable.Loader loader,
@NonNull UrlProcessor urlProcessor,
@NonNull LinkSpan.Resolver resolver,
@NonNull ImageSizeResolver imageSizeResolver
) {
return builderWithDefaults(theme, loader, urlProcessor, resolver, imageSizeResolver)
.build();
return builderWithDefaults(factory, theme, loader, urlProcessor, resolver, imageSizeResolver).build();
}
@NonNull
@ -76,16 +92,27 @@ public class SpannableHtmlParser {
return new Builder();
}
/**
* @since 1.1.0
*/
@NonNull
public static Builder builderWithDefaults(@NonNull SpannableTheme theme) {
return builderWithDefaults(theme, null, null, null, null);
public static Builder builderWithDefaults(@NonNull SpannableFactory factory, @NonNull SpannableTheme theme) {
return builderWithDefaults(
factory,
theme,
null,
null,
null,
null);
}
/**
* Updated in 1.0.1: added imageSizeResolverArgument
* Updated in 1.1.0: add SpannableFactory
*/
@NonNull
public static Builder builderWithDefaults(
@NonNull SpannableFactory factory,
@NonNull SpannableTheme theme,
@Nullable AsyncDrawable.Loader asyncDrawableLoader,
@Nullable UrlProcessor urlProcessor,
@ -101,9 +128,9 @@ public class SpannableHtmlParser {
resolver = new LinkResolverDef();
}
final BoldProvider boldProvider = new BoldProvider();
final ItalicsProvider italicsProvider = new ItalicsProvider();
final StrikeProvider strikeProvider = new StrikeProvider();
final BoldProvider boldProvider = new BoldProvider(factory);
final ItalicsProvider italicsProvider = new ItalicsProvider(factory);
final StrikeProvider strikeProvider = new StrikeProvider(factory);
final ImageProvider imageProvider;
if (asyncDrawableLoader != null) {
@ -112,7 +139,12 @@ public class SpannableHtmlParser {
imageSizeResolver = new ImageSizeResolverDef();
}
imageProvider = new ImageProviderImpl(theme, asyncDrawableLoader, urlProcessor, imageSizeResolver);
imageProvider = new ImageProviderImpl(
factory,
theme,
asyncDrawableLoader,
urlProcessor,
imageSizeResolver);
} else {
imageProvider = null;
}
@ -124,13 +156,13 @@ public class SpannableHtmlParser {
.simpleTag("em", italicsProvider)
.simpleTag("cite", italicsProvider)
.simpleTag("dfn", italicsProvider)
.simpleTag("sup", new SuperScriptProvider(theme))
.simpleTag("sub", new SubScriptProvider(theme))
.simpleTag("u", new UnderlineProvider())
.simpleTag("sup", new SuperScriptProvider(factory, theme))
.simpleTag("sub", new SubScriptProvider(factory, theme))
.simpleTag("u", new UnderlineProvider(factory))
.simpleTag("del", strikeProvider)
.simpleTag("s", strikeProvider)
.simpleTag("strike", strikeProvider)
.simpleTag("a", new LinkProvider(theme, urlProcessor, resolver))
.simpleTag("a", new LinkProvider(factory, theme, urlProcessor, resolver))
.imageProvider(imageProvider);
}

View File

@ -1,11 +1,22 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import android.text.style.StrikethroughSpan;
import ru.noties.markwon.SpannableFactory;
class StrikeProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
/**
* @since 1.1.0
*/
StrikeProvider(@NonNull SpannableFactory factory) {
this.factory = factory;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new StrikethroughSpan();
return factory.strikethrough();
}
}

View File

@ -2,19 +2,21 @@ package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.spans.SpannableTheme;
import ru.noties.markwon.spans.SubScriptSpan;
class SubScriptProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
private final SpannableTheme theme;
public SubScriptProvider(SpannableTheme theme) {
SubScriptProvider(@NonNull SpannableFactory factory, @NonNull SpannableTheme theme) {
this.factory = factory;
this.theme = theme;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new SubScriptSpan(theme);
return factory.subScript(theme);
}
}

View File

@ -2,19 +2,21 @@ package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import ru.noties.markwon.SpannableFactory;
import ru.noties.markwon.spans.SpannableTheme;
import ru.noties.markwon.spans.SuperScriptSpan;
class SuperScriptProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
private final SpannableTheme theme;
SuperScriptProvider(SpannableTheme theme) {
SuperScriptProvider(@NonNull SpannableFactory factory, @NonNull SpannableTheme theme) {
this.factory = factory;
this.theme = theme;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new SuperScriptSpan(theme);
return factory.superScript(theme);
}
}

View File

@ -1,12 +1,22 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import android.text.style.UnderlineSpan;
import ru.noties.markwon.SpannableFactory;
class UnderlineProvider implements SpannableHtmlParser.SpanProvider {
private final SpannableFactory factory;
/**
* @since 1.1.0
*/
UnderlineProvider(@NonNull SpannableFactory factory) {
this.factory = factory;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
return new UnderlineSpan();
return factory.underline();
}
}