Remove old html code

This commit is contained in:
Dimitry Ivanov 2018-08-19 16:02:02 +03:00
parent 9246a53891
commit 20c92ddd5e
11 changed files with 0 additions and 896 deletions

View File

@ -3,7 +3,6 @@ package ru.noties.markwon.renderer;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils;
import org.commonmark.ext.gfm.strikethrough.Strikethrough; import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.ext.gfm.tables.TableBody; import org.commonmark.ext.gfm.tables.TableBody;
@ -35,16 +34,13 @@ import org.commonmark.node.StrongEmphasis;
import org.commonmark.node.Text; import org.commonmark.node.Text;
import org.commonmark.node.ThematicBreak; import org.commonmark.node.ThematicBreak;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.List; 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.html.api.MarkwonHtmlParser;
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;
import ru.noties.markwon.tasklist.TaskListBlock; import ru.noties.markwon.tasklist.TaskListBlock;

View File

@ -1,22 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
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 factory.strongEmphasis();
}
}

View File

@ -1,224 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
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.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;
this.imageSizeResolver = imageSizeResolver;
}
@Override
public Spanned provide(@NonNull SpannableHtmlParser.Tag tag) {
final Spanned spanned;
final Map<String, String> attributes = tag.attributes();
final String src = attributes.get("src");
final String alt = attributes.get("alt");
if (!TextUtils.isEmpty(src)) {
final String destination = urlProcessor.process(src);
final String replacement;
if (!TextUtils.isEmpty(alt)) {
replacement = alt;
} else {
replacement = "\uFFFC";
}
final Object span = factory.image(
theme,
destination,
loader,
imageSizeResolver,
parseImageSize(attributes),
false);
final SpannableString string = new SpannableString(replacement);
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 {
spanned = null;
}
return spanned;
}
@Nullable
private static ImageSize parseImageSize(@NonNull Map<String, String> attributes) {
final ImageSize imageSize;
final StyleProvider styleProvider = new StyleProvider(attributes.get("style"));
final ImageSize.Dimension width = parseDimension(extractDimension("width", attributes, styleProvider));
final ImageSize.Dimension height = parseDimension(extractDimension("height", attributes, styleProvider));
if (width == null
&& height == null) {
imageSize = null;
} else {
imageSize = new ImageSize(width, height);
}
return imageSize;
}
@Nullable
private static String extractDimension(@NonNull String name, @NonNull Map<String, String> attributes, @NonNull StyleProvider styleProvider) {
final String out;
final String inline = attributes.get(name);
if (!TextUtils.isEmpty(inline)) {
out = inline;
} else {
out = extractDimensionFromStyle(name, styleProvider);
}
return out;
}
@Nullable
private static String extractDimensionFromStyle(@NonNull String name, @NonNull StyleProvider styleProvider) {
return styleProvider.attributes().get(name);
}
@Nullable
private static ImageSize.Dimension parseDimension(@Nullable String raw) {
// a set of digits, then dimension unit (allow floating)
final ImageSize.Dimension dimension;
final int length = raw != null
? raw.length()
: 0;
if (length == 0) {
dimension = null;
} else {
// first digit to find -> unit is finished (can be null)
int index = -1;
for (int i = length - 1; i >= 0; i--) {
if (Character.isDigit(raw.charAt(i))) {
index = i;
break;
}
}
// no digits -> no dimension
if (index == -1) {
dimension = null;
} else {
final String value;
final String unit;
// no unit is specified
if (index == length - 1) {
value = raw;
unit = null;
} else {
value = raw.substring(0, index + 1);
unit = raw.substring(index + 1);
}
ImageSize.Dimension inner;
try {
final float floatValue = Float.parseFloat(value);
inner = new ImageSize.Dimension(floatValue, unit);
} catch (NumberFormatException e) {
inner = null;
}
dimension = inner;
}
}
return dimension;
}
private static class StyleProvider {
private final String style;
private Map<String, String> attributes;
StyleProvider(@Nullable String style) {
this.style = style;
}
@NonNull
Map<String, String> attributes() {
final Map<String, String> out;
if (attributes != null) {
out = attributes;
} else {
if (TextUtils.isEmpty(style)) {
out = attributes = Collections.emptyMap();
} else {
final String[] split = style.split(";");
final Map<String, String> map = new HashMap<>(split.length);
String[] parts;
for (String s : split) {
if (!TextUtils.isEmpty(s)) {
parts = s.split(":");
if (parts.length == 2) {
map.put(parts[0].trim(), parts[1].trim());
}
}
}
out = attributes = map;
}
}
return out;
}
}
}

View File

@ -1,22 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
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 factory.emphasis();
}
}

View File

@ -1,49 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
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;
}
@Override
public Object provide(@NonNull SpannableHtmlParser.Tag tag) {
final Object span;
final Map<String, String> attributes = tag.attributes();
final String href = attributes.get("href");
if (!TextUtils.isEmpty(href)) {
final String destination = urlProcessor.process(href);
span = factory.link(theme, destination, resolver);
} else {
span = null;
}
return span;
}
}

View File

@ -1,332 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.annotation.TargetApi;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Html;
import android.text.Spanned;
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;
import ru.noties.markwon.renderer.ImageSizeResolverDef;
import ru.noties.markwon.spans.AsyncDrawable;
import ru.noties.markwon.spans.LinkSpan;
import ru.noties.markwon.spans.SpannableTheme;
@SuppressWarnings("WeakerAccess")
public class SpannableHtmlParser {
/**
* @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(factory, theme, loader, urlProcessor, resolver, imageSizeResolver).build();
}
@NonNull
public static Builder builder() {
return new Builder();
}
/**
* @since 1.1.0
*/
@NonNull
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,
@Nullable LinkSpan.Resolver resolver,
@Nullable ImageSizeResolver imageSizeResolver
) {
if (urlProcessor == null) {
urlProcessor = new UrlProcessorNoOp();
}
if (resolver == null) {
resolver = new LinkResolverDef();
}
final BoldProvider boldProvider = new BoldProvider(factory);
final ItalicsProvider italicsProvider = new ItalicsProvider(factory);
final StrikeProvider strikeProvider = new StrikeProvider(factory);
final ImageProvider imageProvider;
if (asyncDrawableLoader != null) {
if (imageSizeResolver == null) {
imageSizeResolver = new ImageSizeResolverDef();
}
imageProvider = new ImageProviderImpl(
factory,
theme,
asyncDrawableLoader,
urlProcessor,
imageSizeResolver);
} else {
imageProvider = null;
}
return new Builder()
.simpleTag("b", boldProvider)
.simpleTag("strong", boldProvider)
.simpleTag("i", italicsProvider)
.simpleTag("em", italicsProvider)
.simpleTag("cite", italicsProvider)
.simpleTag("dfn", italicsProvider)
.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(factory, theme, urlProcessor, resolver))
.imageProvider(imageProvider);
}
// for simple tags without arguments
// <b>, <i>, etc
public interface SpanProvider {
Object provide(@NonNull Tag tag);
}
public interface ImageProvider {
Spanned provide(@NonNull Tag tag);
}
public interface HtmlParser {
// returns span for a simple content
Object getSpan(@NonNull String html);
Spanned parse(@NonNull String html);
}
private final Map<String, SpanProvider> simpleTags;
private final ImageProvider imageProvider;
private final HtmlParser parser;
private final TagParser tagParser;
private SpannableHtmlParser(Builder builder) {
this.simpleTags = builder.simpleTags;
this.imageProvider = builder.imageProvider;
this.parser = builder.parser;
this.tagParser = new TagParser();
}
@Nullable
public Tag parseTag(String html) {
return tagParser.parse(html);
}
@Nullable
public Object getSpanForTag(@NonNull Tag tag) {
// check if we have specific handler for tag.name
final Object out;
final SpanProvider provider = simpleTags.get(tag.name);
if (provider != null) {
out = provider.provide(tag);
} else {
// let's prepare mock content & extract spans from it
// actual content doesn't matter, here it's just `abc`
final String mock = tag.raw + "abc" + "</" + tag.name + ">";
out = parser.getSpan(mock);
}
return out;
}
// if tag is NULL, then it's HtmlBlock... else just a void tag
public Spanned getSpanned(@Nullable Tag tag, String html) {
final Spanned spanned;
if (tag != null && "img".equals(tag.name) && imageProvider != null) {
spanned = imageProvider.provide(tag);
} else {
spanned = parser.parse(html);
}
return spanned;
}
public static class Builder {
private final Map<String, SpanProvider> simpleTags = new HashMap<>(3);
private ImageProvider imageProvider;
private HtmlParser parser;
@NonNull
Builder simpleTag(@NonNull String tag, @NonNull SpanProvider provider) {
simpleTags.put(tag, provider);
return this;
}
@NonNull
public Builder imageProvider(@Nullable ImageProvider imageProvider) {
this.imageProvider = imageProvider;
return this;
}
@NonNull
public Builder parser(@NonNull HtmlParser parser) {
this.parser = parser;
return this;
}
@NonNull
public SpannableHtmlParser build() {
if (parser == null) {
parser = DefaultHtmlParser.create();
}
return new SpannableHtmlParser(this);
}
}
public static class Tag {
private final String raw;
private final String name;
private final Map<String, String> attributes;
private final boolean opening;
private final boolean voidTag;
public Tag(String raw, String name, @NonNull Map<String, String> attributes, boolean opening, boolean voidTag) {
this.raw = raw;
this.name = name;
this.attributes = attributes;
this.opening = opening;
this.voidTag = voidTag;
}
public String raw() {
return raw;
}
public String name() {
return name;
}
@NonNull
public Map<String, String> attributes() {
return attributes;
}
public boolean opening() {
return opening;
}
public boolean voidTag() {
return voidTag;
}
@Override
public String toString() {
return "Tag{" +
"raw='" + raw + '\'' +
", name='" + name + '\'' +
", attributes=" + attributes +
", opening=" + opening +
", voidTag=" + voidTag +
'}';
}
}
public static abstract class DefaultHtmlParser implements HtmlParser {
public static DefaultHtmlParser create() {
final DefaultHtmlParser parser;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
parser = new Parser24();
} else {
parser = new ParserPre24();
}
return parser;
}
Object getSpan(Spanned spanned) {
final Object out;
final Object[] spans;
final int length = spanned != null ? spanned.length() : 0;
if (length == 0) {
spans = null;
} else {
spans = spanned.getSpans(0, length, Object.class);
}
if (spans != null
&& spans.length > 0) {
out = spans[0];
} else {
out = null;
}
return out;
}
@SuppressWarnings("deprecation")
private static class ParserPre24 extends DefaultHtmlParser {
@Override
public Object getSpan(@NonNull String html) {
return getSpan(parse(html));
}
@Override
public Spanned parse(@NonNull String html) {
return Html.fromHtml(html, null, null);
}
}
@TargetApi(Build.VERSION_CODES.N)
private static class Parser24 extends DefaultHtmlParser {
@Override
public Object getSpan(@NonNull String html) {
return getSpan(parse(html));
}
@Override
public Spanned parse(@NonNull String html) {
return Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT, null, null);
}
}
}
}

View File

@ -1,22 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
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 factory.strikethrough();
}
}

View File

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

View File

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

View File

@ -1,155 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
class TagParser {
private static final Set<String> VOID_TAGS;
static {
final String[] tags = {
"area", "base", "br", "col", "embed", "hr", "img", "input",
"keygen", "link", "meta", "param", "source", "track", "wbr"
};
final Set<String> set = new HashSet<>(tags.length);
Collections.addAll(set, tags);
VOID_TAGS = Collections.unmodifiableSet(set);
}
TagParser() {
}
@Nullable
SpannableHtmlParser.Tag parse(String html) {
final SpannableHtmlParser.Tag tag;
final int length = html != null
? html.length()
: 0;
// absolutely minimum (`<i>`)
if (length < 3) {
tag = null;
} else {
// // okay, we will consider a tag a void one if it's in our void list tag
final boolean closing = '<' == html.charAt(0) && '/' == html.charAt(1);
final boolean voidTag;
Map<String, String> attributes = null;
final StringBuilder builder = new StringBuilder();
String name = null;
String pendingAttribute = null;
char c;
char valueDelimiter = '\0';
for (int i = 0; i < length; i++) {
c = html.charAt(i);
// no more handling
if ('>' == c
|| '\\' == c) {
break;
}
if (name == null) {
if (Character.isSpaceChar(c)) {
//noinspection StatementWithEmptyBody
if (builder.length() == 0) {
// ignore it, we must wait until we have tagName
} else {
name = builder.toString();
// clear buffer
builder.setLength(0);
}
} else {
if (Character.isLetterOrDigit(c)) {
builder.append(c);
} /*else {
// we allow non-letter-digit only if builder.length == 0
// if we have already started
}*/
}
} else if (pendingAttribute == null) {
// we start checking for attribute
// ignore non-letter-digits before
if (Character.isLetterOrDigit(c)) {
builder.append(c);
} else /*if ('=' == c)*/ {
// attribute name is finished (only if we have already added something)
// else it's trailing chars that we are not interested in
if (builder.length() > 0) {
pendingAttribute = builder.toString();
builder.setLength(0);
}
}
} else {
// first char that we will meet will be the delimiter
if (valueDelimiter == '\0') {
valueDelimiter = c;
} else {
if (c == valueDelimiter) {
if (attributes == null) {
attributes = new HashMap<>(3);
}
attributes.put(pendingAttribute, builder.toString());
pendingAttribute = null;
valueDelimiter = '\0';
builder.setLength(0);
} else {
builder.append(c);
}
}
}
}
if (builder.length() > 0) {
if (name == null) {
name = builder.toString();
} else if (pendingAttribute != null) {
if (attributes == null) {
attributes = new HashMap<>(3);
}
attributes.put(pendingAttribute, builder.toString());
}
}
// in case of wrong parsing
if (name == null) {
tag = null;
} else {
voidTag = !closing && VOID_TAGS.contains(name);
final Map<String, String> attributesMap;
if (attributes == null
|| attributes.size() == 0) {
//noinspection unchecked
attributesMap = Collections.EMPTY_MAP;
} else {
attributesMap = Collections.unmodifiableMap(attributes);
}
tag = new SpannableHtmlParser.Tag(html, name, attributesMap, !closing, voidTag);
}
}
return tag;
}
}

View File

@ -1,22 +0,0 @@
package ru.noties.markwon.renderer.html;
import android.support.annotation.NonNull;
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 factory.underline();
}
}