Reuse HTML named entities w commonmark-java (less memory consumption)
This commit is contained in:
		
							parent
							
								
									d382b37a72
								
							
						
					
					
						commit
						dc5cc9471c
					
				| @ -2,8 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| # Markwon | # Markwon | ||||||
| 
 | 
 | ||||||
| & © † ‡ |  | ||||||
| 
 |  | ||||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon%22) | [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon%22) | ||||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-image-loader%22) | [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-image-loader%22) | ||||||
| [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22) | [](http://search.maven.org/#search|ga|1|g%3A%22ru.noties%22%20AND%20a%3A%22markwon-syntax-highlight%22) | ||||||
|  | |||||||
| @ -13,7 +13,6 @@ import java.util.concurrent.Future; | |||||||
| import javax.inject.Inject; | import javax.inject.Inject; | ||||||
| 
 | 
 | ||||||
| import ru.noties.debug.Debug; | import ru.noties.debug.Debug; | ||||||
| import ru.noties.markwon.html.api.MarkwonHtmlParser; |  | ||||||
| import ru.noties.markwon.spans.AsyncDrawable; | import ru.noties.markwon.spans.AsyncDrawable; | ||||||
| import ru.noties.markwon.spans.SpannableTheme; | import ru.noties.markwon.spans.SpannableTheme; | ||||||
| import ru.noties.markwon.syntax.Prism4jSyntaxHighlight; | import ru.noties.markwon.syntax.Prism4jSyntaxHighlight; | ||||||
| @ -98,7 +97,6 @@ public class MarkdownRenderer { | |||||||
|                                 .build()) |                                 .build()) | ||||||
|                         .factory(new GifAwareSpannableFactory(gifPlaceholder)) |                         .factory(new GifAwareSpannableFactory(gifPlaceholder)) | ||||||
|                         .trimWhiteSpaceEnd(false) |                         .trimWhiteSpaceEnd(false) | ||||||
|                         .htmlParser(MarkwonHtmlParser.noOp()) |  | ||||||
|                         .build(); |                         .build(); | ||||||
| 
 | 
 | ||||||
|                 final long start = SystemClock.uptimeMillis(); |                 final long start = SystemClock.uptimeMillis(); | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ dependencies { | |||||||
| 
 | 
 | ||||||
|     deps.with { |     deps.with { | ||||||
|         api it['support-annotations'] |         api it['support-annotations'] | ||||||
|  |         api it['commonmark'] | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     deps.test.with { |     deps.test.with { | ||||||
|  | |||||||
| @ -0,0 +1,50 @@ | |||||||
|  | package ru.noties.markwon.html.impl.jsoup.nodes; | ||||||
|  | 
 | ||||||
|  | import android.support.annotation.NonNull; | ||||||
|  | 
 | ||||||
|  | import org.commonmark.internal.util.Html5Entities; | ||||||
|  | 
 | ||||||
|  | import java.lang.reflect.Field; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.Map; | ||||||
|  | 
 | ||||||
|  | public abstract class CommonMarkEntities { | ||||||
|  | 
 | ||||||
|  |     public static boolean isNamedEntity(@NonNull String name) { | ||||||
|  |         return COMMONMARK_NAMED_ENTITIES.containsKey(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static int codepointsForName(@NonNull String name, @NonNull int[] codepoints) { | ||||||
|  |         final String value = COMMONMARK_NAMED_ENTITIES.get(name); | ||||||
|  |         if (value != null) { | ||||||
|  |             final int length = value.length(); | ||||||
|  |             if (length == 1) { | ||||||
|  |                 codepoints[0] = value.charAt(0); | ||||||
|  |             } else { | ||||||
|  |                 codepoints[0] = value.charAt(0); | ||||||
|  |                 codepoints[1] = value.charAt(1); | ||||||
|  |             } | ||||||
|  |             return length; | ||||||
|  |         } | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static final Map<String, String> COMMONMARK_NAMED_ENTITIES; | ||||||
|  | 
 | ||||||
|  |     static { | ||||||
|  |         Map<String, String> map; | ||||||
|  |         try { | ||||||
|  |             final Field field = Html5Entities.class.getDeclaredField("NAMED_CHARACTER_REFERENCES"); | ||||||
|  |             field.setAccessible(true); | ||||||
|  |             //noinspection unchecked | ||||||
|  |             map = (Map<String, String>) field.get(null); | ||||||
|  |         } catch (Throwable t) { | ||||||
|  |             map = Collections.emptyMap(); | ||||||
|  |             t.printStackTrace(); | ||||||
|  |         } | ||||||
|  |         COMMONMARK_NAMED_ENTITIES = map; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private CommonMarkEntities() { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,351 +0,0 @@ | |||||||
| package ru.noties.markwon.html.impl.jsoup.nodes; |  | ||||||
| 
 |  | ||||||
| import java.nio.charset.CharsetEncoder; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.HashMap; |  | ||||||
| 
 |  | ||||||
| import ru.noties.markwon.html.impl.jsoup.helper.Validate; |  | ||||||
| import ru.noties.markwon.html.impl.jsoup.parser.CharacterReader; |  | ||||||
| 
 |  | ||||||
| import static ru.noties.markwon.html.impl.jsoup.nodes.Entities.EscapeMode.base; |  | ||||||
| import static ru.noties.markwon.html.impl.jsoup.nodes.Entities.EscapeMode.extended; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * HTML entities, and escape routines. Source: <a href="http://www.w3.org/TR/html5/named-character-references.html#named-character-references">W3C |  | ||||||
|  * HTML named character references</a>. |  | ||||||
|  */ |  | ||||||
| public class Entities { |  | ||||||
|     private static final int empty = -1; |  | ||||||
|     private static final String emptyName = ""; |  | ||||||
|     static final int codepointRadix = 36; |  | ||||||
|     private static final char[] codeDelims = {',', ';'}; |  | ||||||
|     private static final HashMap<String, String> multipoints = new HashMap<>(); // name -> multiple character references |  | ||||||
| //    private static final Document.OutputSettings DefaultOutput = new Document.OutputSettings(); |  | ||||||
| 
 |  | ||||||
|     public enum EscapeMode { |  | ||||||
|         /** |  | ||||||
|          * Restricted entities suitable for XHTML output: lt, gt, amp, and quot only. |  | ||||||
|          */ |  | ||||||
|         xhtml(EntitiesData.xmlPoints, 4), |  | ||||||
|         /** |  | ||||||
|          * Default HTML output entities. |  | ||||||
|          */ |  | ||||||
|         base(EntitiesData.basePoints, 106), |  | ||||||
|         /** |  | ||||||
|          * Complete HTML entities. |  | ||||||
|          */ |  | ||||||
|         extended(EntitiesData.fullPoints, 2125); |  | ||||||
| 
 |  | ||||||
|         // table of named references to their codepoints. sorted so we can binary search. built by BuildEntities. |  | ||||||
|         private String[] nameKeys; |  | ||||||
|         private int[] codeVals; // limitation is the few references with multiple characters; those go into multipoints. |  | ||||||
| 
 |  | ||||||
|         // table of codepoints to named entities. |  | ||||||
|         private int[] codeKeys; // we don' support multicodepoints to single named value currently |  | ||||||
|         private String[] nameVals; |  | ||||||
| 
 |  | ||||||
|         EscapeMode(String file, int size) { |  | ||||||
|             load(this, file, size); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         int codepointForName(final String name) { |  | ||||||
|             int index = Arrays.binarySearch(nameKeys, name); |  | ||||||
|             return index >= 0 ? codeVals[index] : empty; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         String nameForCodepoint(final int codepoint) { |  | ||||||
|             final int index = Arrays.binarySearch(codeKeys, codepoint); |  | ||||||
|             if (index >= 0) { |  | ||||||
|                 // the results are ordered so lower case versions of same codepoint come after uppercase, and we prefer to emit lower |  | ||||||
|                 // (and binary search for same item with multi results is undefined |  | ||||||
|                 return (index < nameVals.length - 1 && codeKeys[index + 1] == codepoint) ? |  | ||||||
|                         nameVals[index + 1] : nameVals[index]; |  | ||||||
|             } |  | ||||||
|             return emptyName; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private int size() { |  | ||||||
|             return nameKeys.length; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private Entities() { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Check if the input is a known named entity |  | ||||||
|      * |  | ||||||
|      * @param name the possible entity name (e.g. "lt" or "amp") |  | ||||||
|      * @return true if a known named entity |  | ||||||
|      */ |  | ||||||
|     public static boolean isNamedEntity(final String name) { |  | ||||||
|         return extended.codepointForName(name) != empty; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Check if the input is a known named entity in the base entity set. |  | ||||||
|      * |  | ||||||
|      * @param name the possible entity name (e.g. "lt" or "amp") |  | ||||||
|      * @return true if a known named entity in the base set |  | ||||||
|      * @see #isNamedEntity(String) |  | ||||||
|      */ |  | ||||||
|     public static boolean isBaseNamedEntity(final String name) { |  | ||||||
|         return base.codepointForName(name) != empty; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the Character value of the named entity |  | ||||||
|      * |  | ||||||
|      * @param name named entity (e.g. "lt" or "amp") |  | ||||||
|      * @return the Character value of the named entity (e.g. '{@literal <}' or '{@literal &}') |  | ||||||
|      * @deprecated does not support characters outside the BMP or multiple character names |  | ||||||
|      */ |  | ||||||
|     public static Character getCharacterByName(String name) { |  | ||||||
|         return (char) extended.codepointForName(name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get the character(s) represented by the named entity |  | ||||||
|      * |  | ||||||
|      * @param name entity (e.g. "lt" or "amp") |  | ||||||
|      * @return the string value of the character(s) represented by this entity, or "" if not defined |  | ||||||
|      */ |  | ||||||
|     public static String getByName(String name) { |  | ||||||
|         String val = multipoints.get(name); |  | ||||||
|         if (val != null) |  | ||||||
|             return val; |  | ||||||
|         int codepoint = extended.codepointForName(name); |  | ||||||
|         if (codepoint != empty) |  | ||||||
|             return new String(new int[]{codepoint}, 0, 1); |  | ||||||
|         return emptyName; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static int codepointsForName(final String name, final int[] codepoints) { |  | ||||||
|         String val = multipoints.get(name); |  | ||||||
|         if (val != null) { |  | ||||||
|             codepoints[0] = val.codePointAt(0); |  | ||||||
|             codepoints[1] = val.codePointAt(1); |  | ||||||
|             return 2; |  | ||||||
|         } |  | ||||||
|         int codepoint = extended.codepointForName(name); |  | ||||||
|         if (codepoint != empty) { |  | ||||||
|             codepoints[0] = codepoint; |  | ||||||
|             return 1; |  | ||||||
|         } |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| //    /** |  | ||||||
| //     * HTML escape an input string. That is, {@code <} is returned as {@code <} |  | ||||||
| //     * |  | ||||||
| //     * @param string the un-escaped string to escape |  | ||||||
| //     * @param out the output settings to use |  | ||||||
| //     * @return the escaped string |  | ||||||
| //     */ |  | ||||||
| //    public static String escape(String string, Document.OutputSettings out) { |  | ||||||
| //        if (string == null) |  | ||||||
| //            return ""; |  | ||||||
| //        StringBuilder accum = new StringBuilder(string.length() * 2); |  | ||||||
| //        try { |  | ||||||
| //            escape(accum, string, out, false, false, false); |  | ||||||
| //        } catch (IOException e) { |  | ||||||
| //            throw new SerializationException(e); // doesn't happen |  | ||||||
| //        } |  | ||||||
| //        return accum.toString(); |  | ||||||
| //    } |  | ||||||
| 
 |  | ||||||
| //    /** |  | ||||||
| //     * HTML escape an input string, using the default settings (UTF-8, base entities). That is, {@code <} is returned as |  | ||||||
| //     * {@code <} |  | ||||||
| //     * |  | ||||||
| //     * @param string the un-escaped string to escape |  | ||||||
| //     * @return the escaped string |  | ||||||
| //     */ |  | ||||||
| //    public static String escape(String string) { |  | ||||||
| //        return escape(string, DefaultOutput); |  | ||||||
| //    } |  | ||||||
| // |  | ||||||
| //    // this method is ugly, and does a lot. but other breakups cause rescanning and stringbuilder generations |  | ||||||
| //    static void escape(Appendable accum, String string, Document.OutputSettings out, |  | ||||||
| //                       boolean inAttribute, boolean normaliseWhite, boolean stripLeadingWhite) throws IOException { |  | ||||||
| // |  | ||||||
| //        boolean lastWasWhite = false; |  | ||||||
| //        boolean reachedNonWhite = false; |  | ||||||
| //        final EscapeMode escapeMode = out.escapeMode(); |  | ||||||
| //        final CharsetEncoder encoder = out.encoder(); |  | ||||||
| //        final CoreCharset coreCharset = out.coreCharset; // init in out.prepareEncoder() |  | ||||||
| //        final int length = string.length(); |  | ||||||
| // |  | ||||||
| //        int codePoint; |  | ||||||
| //        for (int offset = 0; offset < length; offset += Character.charCount(codePoint)) { |  | ||||||
| //            codePoint = string.codePointAt(offset); |  | ||||||
| // |  | ||||||
| //            if (normaliseWhite) { |  | ||||||
| //                if (StringUtil.isWhitespace(codePoint)) { |  | ||||||
| //                    if ((stripLeadingWhite && !reachedNonWhite) || lastWasWhite) |  | ||||||
| //                        continue; |  | ||||||
| //                    accum.append(' '); |  | ||||||
| //                    lastWasWhite = true; |  | ||||||
| //                    continue; |  | ||||||
| //                } else { |  | ||||||
| //                    lastWasWhite = false; |  | ||||||
| //                    reachedNonWhite = true; |  | ||||||
| //                } |  | ||||||
| //            } |  | ||||||
| //            // surrogate pairs, split implementation for efficiency on single char common case (saves creating strings, char[]): |  | ||||||
| //            if (codePoint < Character.MIN_SUPPLEMENTARY_CODE_POINT) { |  | ||||||
| //                final char c = (char) codePoint; |  | ||||||
| //                // html specific and required escapes: |  | ||||||
| //                switch (c) { |  | ||||||
| //                    case '&': |  | ||||||
| //                        accum.append("&"); |  | ||||||
| //                        break; |  | ||||||
| //                    case 0xA0: |  | ||||||
| //                        if (escapeMode != EscapeMode.xhtml) |  | ||||||
| //                            accum.append(" "); |  | ||||||
| //                        else |  | ||||||
| //                            accum.append(" "); |  | ||||||
| //                        break; |  | ||||||
| //                    case '<': |  | ||||||
| //                        // escape when in character data or when in a xml attribue val; not needed in html attr val |  | ||||||
| //                        if (!inAttribute || escapeMode == EscapeMode.xhtml) |  | ||||||
| //                            accum.append("<"); |  | ||||||
| //                        else |  | ||||||
| //                            accum.append(c); |  | ||||||
| //                        break; |  | ||||||
| //                    case '>': |  | ||||||
| //                        if (!inAttribute) |  | ||||||
| //                            accum.append(">"); |  | ||||||
| //                        else |  | ||||||
| //                            accum.append(c); |  | ||||||
| //                        break; |  | ||||||
| //                    case '"': |  | ||||||
| //                        if (inAttribute) |  | ||||||
| //                            accum.append("""); |  | ||||||
| //                        else |  | ||||||
| //                            accum.append(c); |  | ||||||
| //                        break; |  | ||||||
| //                    default: |  | ||||||
| //                        if (canEncode(coreCharset, c, encoder)) |  | ||||||
| //                            accum.append(c); |  | ||||||
| //                        else |  | ||||||
| //                            appendEncoded(accum, escapeMode, codePoint); |  | ||||||
| //                } |  | ||||||
| //            } else { |  | ||||||
| //                final String c = new String(Character.toChars(codePoint)); |  | ||||||
| //                if (encoder.canEncode(c)) // uses fallback encoder for simplicity |  | ||||||
| //                    accum.append(c); |  | ||||||
| //                else |  | ||||||
| //                    appendEncoded(accum, escapeMode, codePoint); |  | ||||||
| //            } |  | ||||||
| //        } |  | ||||||
| //    } |  | ||||||
| 
 |  | ||||||
| //    private static void appendEncoded(Appendable accum, EscapeMode escapeMode, int codePoint) throws IOException { |  | ||||||
| //        final String name = escapeMode.nameForCodepoint(codePoint); |  | ||||||
| //        if (name != emptyName) // ok for identity check |  | ||||||
| //            accum.append('&').append(name).append(';'); |  | ||||||
| //        else |  | ||||||
| //            accum.append("&#x").append(Integer.toHexString(codePoint)).append(';'); |  | ||||||
| //    } |  | ||||||
| 
 |  | ||||||
| //    /** |  | ||||||
| //     * Un-escape an HTML escaped string. That is, {@code <} is returned as {@code <}. |  | ||||||
| //     * |  | ||||||
| //     * @param string the HTML string to un-escape |  | ||||||
| //     * @return the unescaped string |  | ||||||
| //     */ |  | ||||||
| //    public static String unescape(String string) { |  | ||||||
| //        return unescape(string, false); |  | ||||||
| //    } |  | ||||||
| 
 |  | ||||||
| //    /** |  | ||||||
| //     * Unescape the input string. |  | ||||||
| //     * |  | ||||||
| //     * @param string to un-HTML-escape |  | ||||||
| //     * @param strict if "strict" (that is, requires trailing ';' char, otherwise that's optional) |  | ||||||
| //     * @return unescaped string |  | ||||||
| //     */ |  | ||||||
| //    static String unescape(String string, boolean strict) { |  | ||||||
| //        return Parser.unescapeEntities(string, strict); |  | ||||||
| //    } |  | ||||||
| 
 |  | ||||||
|     /* |  | ||||||
|      * Provides a fast-path for Encoder.canEncode, which drastically improves performance on Android post JellyBean. |  | ||||||
|      * After KitKat, the implementation of canEncode degrades to the point of being useless. For non ASCII or UTF, |  | ||||||
|      * performance may be bad. We can add more encoders for common character sets that are impacted by performance |  | ||||||
|      * issues on Android if required. |  | ||||||
|      * |  | ||||||
|      * Benchmarks:     * |  | ||||||
|      * OLD toHtml() impl v New (fastpath) in millis |  | ||||||
|      * Wiki: 1895, 16 |  | ||||||
|      * CNN: 6378, 55 |  | ||||||
|      * Alterslash: 3013, 28 |  | ||||||
|      * Jsoup: 167, 2 |  | ||||||
|      */ |  | ||||||
|     private static boolean canEncode(final CoreCharset charset, final char c, final CharsetEncoder fallback) { |  | ||||||
|         // todo add more charset tests if impacted by Android's bad perf in canEncode |  | ||||||
|         switch (charset) { |  | ||||||
|             case ascii: |  | ||||||
|                 return c < 0x80; |  | ||||||
|             case utf: |  | ||||||
|                 return true; // real is:!(Character.isLowSurrogate(c) || Character.isHighSurrogate(c)); - but already check above |  | ||||||
|             default: |  | ||||||
|                 return fallback.canEncode(c); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     enum CoreCharset { |  | ||||||
|         ascii, utf, fallback; |  | ||||||
| 
 |  | ||||||
|         static CoreCharset byName(final String name) { |  | ||||||
|             if (name.equals("US-ASCII")) |  | ||||||
|                 return ascii; |  | ||||||
|             if (name.startsWith("UTF-")) // covers UTF-8, UTF-16, et al |  | ||||||
|                 return utf; |  | ||||||
|             return fallback; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static void load(EscapeMode e, String pointsData, int size) { |  | ||||||
|         e.nameKeys = new String[size]; |  | ||||||
|         e.codeVals = new int[size]; |  | ||||||
|         e.codeKeys = new int[size]; |  | ||||||
|         e.nameVals = new String[size]; |  | ||||||
| 
 |  | ||||||
|         int i = 0; |  | ||||||
|         CharacterReader reader = new CharacterReader(pointsData); |  | ||||||
| 
 |  | ||||||
|         while (!reader.isEmpty()) { |  | ||||||
|             // NotNestedLessLess=10913,824;1887& |  | ||||||
| 
 |  | ||||||
|             final String name = reader.consumeTo('='); |  | ||||||
|             reader.advance(); |  | ||||||
|             final int cp1 = Integer.parseInt(reader.consumeToAny(codeDelims), codepointRadix); |  | ||||||
|             final char codeDelim = reader.current(); |  | ||||||
|             reader.advance(); |  | ||||||
|             final int cp2; |  | ||||||
|             if (codeDelim == ',') { |  | ||||||
|                 cp2 = Integer.parseInt(reader.consumeTo(';'), codepointRadix); |  | ||||||
|                 reader.advance(); |  | ||||||
|             } else { |  | ||||||
|                 cp2 = empty; |  | ||||||
|             } |  | ||||||
|             final String indexS = reader.consumeTo('&'); |  | ||||||
|             final int index = Integer.parseInt(indexS, codepointRadix); |  | ||||||
|             reader.advance(); |  | ||||||
| 
 |  | ||||||
|             e.nameKeys[i] = name; |  | ||||||
|             e.codeVals[i] = cp1; |  | ||||||
|             e.codeKeys[index] = cp1; |  | ||||||
|             e.nameVals[index] = name; |  | ||||||
| 
 |  | ||||||
|             if (cp2 != empty) { |  | ||||||
|                 multipoints.put(name, new String(new int[]{cp1, cp2}, 0, 2)); |  | ||||||
|             } |  | ||||||
|             i++; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Validate.isTrue(i == size, "Unexpected count of entities loaded"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -3,7 +3,7 @@ package ru.noties.markwon.html.impl.jsoup.parser; | |||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| 
 | 
 | ||||||
| import ru.noties.markwon.html.impl.jsoup.helper.Validate; | import ru.noties.markwon.html.impl.jsoup.helper.Validate; | ||||||
| import ru.noties.markwon.html.impl.jsoup.nodes.Entities; | import ru.noties.markwon.html.impl.jsoup.nodes.CommonMarkEntities; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Readers the input stream into tokens. |  * Readers the input stream into tokens. | ||||||
| @ -175,7 +175,7 @@ public final class Tokeniser { | |||||||
|             String nameRef = reader.consumeLetterThenDigitSequence(); |             String nameRef = reader.consumeLetterThenDigitSequence(); | ||||||
|             boolean looksLegit = reader.matches(';'); |             boolean looksLegit = reader.matches(';'); | ||||||
|             // found if a base named entity without a ;, or an extended entity with the ;. |             // found if a base named entity without a ;, or an extended entity with the ;. | ||||||
|             boolean found = (Entities.isBaseNamedEntity(nameRef) || (Entities.isNamedEntity(nameRef) && looksLegit)); |             boolean found = (CommonMarkEntities.isNamedEntity(nameRef) && looksLegit); | ||||||
| 
 | 
 | ||||||
|             if (!found) { |             if (!found) { | ||||||
|                 reader.rewindToMark(); |                 reader.rewindToMark(); | ||||||
| @ -190,7 +190,7 @@ public final class Tokeniser { | |||||||
|             } |             } | ||||||
|             if (!reader.matchConsume(";")) |             if (!reader.matchConsume(";")) | ||||||
|                 characterReferenceError("missing semicolon"); // missing semi |                 characterReferenceError("missing semicolon"); // missing semi | ||||||
|             int numChars = Entities.codepointsForName(nameRef, multipointHolder); |             int numChars = CommonMarkEntities.codepointsForName(nameRef, multipointHolder); | ||||||
|             if (numChars == 1) { |             if (numChars == 1) { | ||||||
|                 codeRef[0] = multipointHolder[0]; |                 codeRef[0] = multipointHolder[0]; | ||||||
|                 return codeRef; |                 return codeRef; | ||||||
|  | |||||||
| @ -0,0 +1,22 @@ | |||||||
|  | package ru.noties.markwon.html.impl.jsoup.nodes; | ||||||
|  | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | import org.robolectric.RobolectricTestRunner; | ||||||
|  | import org.robolectric.annotation.Config; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.assertEquals; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
|  | 
 | ||||||
|  | @RunWith(RobolectricTestRunner.class) | ||||||
|  | @Config(manifest = Config.NONE) | ||||||
|  | public class CommonMarkEntitiesTest { | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void can_access_field() { | ||||||
|  |         assertTrue("&", CommonMarkEntities.isNamedEntity("amp")); | ||||||
|  |         final int[] codepoints = new int[1]; | ||||||
|  |         CommonMarkEntities.codepointsForName("amp", codepoints); | ||||||
|  |         assertEquals('&', codepoints[0]); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -244,7 +244,7 @@ public class SpannableConfiguration { | |||||||
|          * @param htmlIgnoreNonClosedTags that indicates if non-closed html tags should be kept open. |          * @param htmlIgnoreNonClosedTags that indicates if non-closed html tags should be kept open. | ||||||
|          *                                If this argument is false then all non-closed HTML tags |          *                                If this argument is false then all non-closed HTML tags | ||||||
|          *                                will be closed at the end of a document. Otherwise they will |          *                                will be closed at the end of a document. Otherwise they will | ||||||
|          *                                be delivered non-closed {@link HtmlTag#isClosed()} |          *                                be delivered non-closed {@code HtmlTag#isClosed()} | ||||||
|          * @since 2.0.0 |          * @since 2.0.0 | ||||||
|          */ |          */ | ||||||
|         @NonNull |         @NonNull | ||||||
|  | |||||||
| @ -2,8 +2,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.util.Log; |  | ||||||
| 
 | 
 | ||||||
| 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; | ||||||
| @ -455,7 +453,6 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | |||||||
| 
 | 
 | ||||||
|     private void visitHtml(@Nullable String html) { |     private void visitHtml(@Nullable String html) { | ||||||
|         if (html != null) { |         if (html != null) { | ||||||
|             Log.e("HTML", html); |  | ||||||
|             htmlParser.processFragment(builder, html); |             htmlParser.processFragment(builder, html); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov