From c57d06dc7dc135488c771a2c1d4eff789e20dd2f Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Sat, 25 Aug 2018 13:11:13 +0300 Subject: [PATCH] image-loader add datauri parser --- markwon-image-loader/build.gradle | 5 + .../java/ru/noties/markwon/il/DataUri.java | 60 +++++++++ .../ru/noties/markwon/il/DataUriParser.java | 79 ++++++++++++ .../noties/markwon/il/DataUriParserTest.java | 119 ++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUri.java create mode 100644 markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUriParser.java create mode 100644 markwon-image-loader/src/test/java/ru/noties/markwon/il/DataUriParserTest.java diff --git a/markwon-image-loader/build.gradle b/markwon-image-loader/build.gradle index 4a8103ca..ba05770b 100644 --- a/markwon-image-loader/build.gradle +++ b/markwon-image-loader/build.gradle @@ -27,6 +27,11 @@ dependencies { api it['android-gif'] api it['okhttp'] } + + deps['test'].with { + testImplementation it['junit'] + testImplementation it['robolectric'] + } } afterEvaluate { diff --git a/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUri.java b/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUri.java new file mode 100644 index 00000000..697b7b2e --- /dev/null +++ b/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUri.java @@ -0,0 +1,60 @@ +package ru.noties.markwon.il; + +import android.support.annotation.Nullable; + +public class DataUri { + + private final String contentType; + private final boolean base64; + private final String data; + + public DataUri(@Nullable String contentType, boolean base64, @Nullable String data) { + this.contentType = contentType; + this.base64 = base64; + this.data = data; + } + + @Nullable + public String contentType() { + return contentType; + } + + public boolean base64() { + return base64; + } + + @Nullable + public String data() { + return data; + } + + @Override + public String toString() { + return "DataUri{" + + "contentType='" + contentType + '\'' + + ", base64=" + base64 + + ", data='" + data + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DataUri dataUri = (DataUri) o; + + if (base64 != dataUri.base64) return false; + if (contentType != null ? !contentType.equals(dataUri.contentType) : dataUri.contentType != null) + return false; + return data != null ? data.equals(dataUri.data) : dataUri.data == null; + } + + @Override + public int hashCode() { + int result = contentType != null ? contentType.hashCode() : 0; + result = 31 * result + (base64 ? 1 : 0); + result = 31 * result + (data != null ? data.hashCode() : 0); + return result; + } +} diff --git a/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUriParser.java b/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUriParser.java new file mode 100644 index 00000000..63744a42 --- /dev/null +++ b/markwon-image-loader/src/main/java/ru/noties/markwon/il/DataUriParser.java @@ -0,0 +1,79 @@ +package ru.noties.markwon.il; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public abstract class DataUriParser { + + @Nullable + public abstract DataUri parse(@NonNull String input); + + + @NonNull + public static DataUriParser create() { + return new Impl(); + } + + static class Impl extends DataUriParser { + + @Nullable + @Override + public DataUri parse(@NonNull String input) { + + final int index = input.indexOf(','); + // we expect exactly one comma + if (index < 0) { + return null; + } + + final String contentType; + final boolean base64; + + if (index > 0) { + final String part = input.substring(0, index); + final String[] parts = part.split(";"); + final int length = parts.length; + if (length > 0) { + // if one: either content-type or base64 + if (length == 1) { + final String value = parts[0]; + if ("base64".equals(value)) { + contentType = null; + base64 = true; + } else { + contentType = value.indexOf('/') > -1 + ? value + : null; + base64 = false; + } + } else { + contentType = parts[0].indexOf('/') > -1 + ? parts[0] + : null; + base64 = "base64".equals(parts[length - 1]); + } + } else { + contentType = null; + base64 = false; + } + } else { + contentType = null; + base64 = false; + } + + final String data; + if (index < input.length()) { + final String value = input.substring(index + 1, input.length()).replaceAll("\n", ""); + if (value.length() == 0) { + data = null; + } else { + data = value; + } + } else { + data = null; + } + + return new DataUri(contentType, base64, data); + } + } +} diff --git a/markwon-image-loader/src/test/java/ru/noties/markwon/il/DataUriParserTest.java b/markwon-image-loader/src/test/java/ru/noties/markwon/il/DataUriParserTest.java new file mode 100644 index 00000000..6de01af5 --- /dev/null +++ b/markwon-image-loader/src/test/java/ru/noties/markwon/il/DataUriParserTest.java @@ -0,0 +1,119 @@ +package ru.noties.markwon.il; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class DataUriParserTest { + + private DataUriParser.Impl impl; + + @Before + public void before() { + impl = new DataUriParser.Impl(); + } + + @Test + public void test() { + + final Map data = new LinkedHashMap() {{ + put(",", new DataUri(null, false, null)); + put("image/svg+xml;base64,!@#$%^&*(", new DataUri("image/svg+xml", true, "!@#$%^&*(")); + put("text/vnd-example+xyz;foo=bar;base64,R0lGODdh", new DataUri("text/vnd-example+xyz", true, "R0lGODdh")); + put("text/plain;charset=UTF-8;page=21,the%20data:1234,5678", new DataUri("text/plain", false, "the%20data:1234,5678")); + }}; + + for (Map.Entry entry : data.entrySet()) { + assertEquals(entry.getKey(), entry.getValue(), impl.parse(entry.getKey())); + } + } + + @Test + public void data_new_lines_are_ignored() { + + final String input = "image/png;base64,iVBORw0KGgoAAA\n" + + "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4\n" + + "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU\n" + + "5ErkJggg=="; + + assertEquals( + new DataUri("image/png", true, "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="), + impl.parse(input) + ); + } + + @Test + public void no_comma_returns_null() { + + final String[] inputs = { + "", + "what-ever", + ";;;;;;;", + "some crazy data" + }; + + for (String input : inputs) { + assertNull(input, impl.parse(input)); + } + } + + @Test + public void two_commas() { + final String input = ",,"; // <- second one would be considered data... + assertEquals( + input, + new DataUri(null, false, ","), + impl.parse(input) + ); + } + + @Test + public void more_commas() { + final String input = "first,second,third"; // <- first is just a value (will be ignored) + assertEquals( + input, + new DataUri(null, false, "second,third"), + impl.parse(input) + ); + } + + @Test + public void base64_no_content_type() { + final String input = ";base64,12345"; + assertEquals( + input, + new DataUri(null, true, "12345"), + impl.parse(input) + ); + } + + @Test + public void not_base64_no_content_type() { + final String input = ",qweRTY"; + assertEquals( + input, + new DataUri(null, false, "qweRTY"), + impl.parse(input) + ); + } + + @Test + public void content_type_data_no_base64() { + final String input = "image/png,aSdFg"; + assertEquals( + input, + new DataUri("image/png", false, "aSdFg"), + impl.parse(input) + ); + } +} \ No newline at end of file