@NonNull public static MarkwonHtmlParserImpl create() { return create(HtmlEmptyTagReplacement.create()); }
@NonNull public static MarkwonHtmlParserImpl create(@NonNull HtmlEmptyTagReplacement inlineTagReplacement) { return new MarkwonHtmlParserImpl(inlineTagReplacement, TrimmingAppender.create()); }
if (isBlockTag(name)) { isInsidePreTag = "pre".equals(name); ensureNewLine(output); } else { ensureNewLineIfPreviousWasBlock(output); final HtmlTagImpl.BlockImpl block = HtmlTagImpl.BlockImpl.create(name, start, extractAttributes(startTag), currentBlock); final boolean isVoid = isVoidTag(name) || startTag.selfClosing; if (isVoid) { final String replacement = emptyTagReplacement.replace(block); appendBlockChild(block.parent, block);
protected <T extends Appendable & CharSequence> void processInlineTagEnd( @NonNull T output, @NonNull Token.EndTag endTag) { // try to find it, if none found -> ignore final HtmlTagImpl.InlineImpl openInline = findOpenInlineTag(endTag.normalName); if (openInline != null) { // okay, if this tag is empty -> call replacement if (isEmpty(output, openInline)) { appendEmptyTagReplacement(output, openInline); } // close open inline tag openInline.closeAt(output.length()); } }
protected <T extends Appendable & CharSequence> void processBlockTagEnd( @NonNull T output, @NonNull Token.EndTag endTag) { final String name = endTag.normalName; final HtmlTagImpl.BlockImpl block = findOpenBlockTag(endTag.normalName); if (block != null) { if ("pre".equals(name)) { isInsidePreTag = false; } // okay, if this tag is empty -> call replacement if (isEmpty(output, block)) { appendEmptyTagReplacement(output, block); } block.closeAt(output.length()); // if it's empty -> we do no care about if it's block or not if (!block.isEmpty()) { previousIsBlock = isBlockTag(block.name); } if (TAG_PARAGRAPH.equals(name)) { appendQuietly(output, '\n'); } this.currentBlock = block.parent; } }
protected <T extends Appendable & CharSequence> void processInlineTagStart( @NonNull T output, @NonNull Token.StartTag startTag) { final String name = startTag.normalName; final HtmlTagImpl.InlineImpl inline = new HtmlTagImpl.InlineImpl(name, output.length(), extractAttributes(startTag)); ensureNewLineIfPreviousWasBlock(output); if (isVoidTag(name) || startTag.selfClosing) { final String replacement = emptyTagReplacement.replace(inline); if (replacement != null && replacement.length() > 0) { appendQuietly(output, replacement); } // the thing is: we will keep this inline tag in the list, // but in case of void-tag that has no replacement, there will be no // possibility to set a span (requires at least one char) inline.closeAt(output.length()); } inlineTags.add(inline); }
@Test public void resetClearsBothInlinesAndBlocks() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<p>paragraph <i>italic</i></p><div>div</div>"); impl.reset(); final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction(); final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction(); impl.flushInlineTags(output.length(), inlineTagsAction); impl.flushBlockTags(output.length(), blockTagsAction); assertTrue(inlineTagsAction.called); assertTrue(blockTagsAction.called); assertEquals(0, inlineTagsAction.tags.size()); assertEquals(0, blockTagsAction.tags.size()); }
@Test public void attributes() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<my-tag " + "name=no-name " + ":click='doSomething' " + impl.flushBlockTags(output.length(), action);
@Test public void attributesAreLowerCase() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<i CLASS=\"my-class\" dIsAbLeD @HeLLo=\"there\">"); final CaptureInlineTagsAction action = new CaptureInlineTagsAction(); impl.flushInlineTags(output.length(), action); assertTrue(action.called); assertEquals(1, action.tags.size()); with(action.tags.get(0), new Action<HtmlTag.Inline>() { @Override public void apply(@NonNull HtmlTag.Inline inline) { assertEquals("i", inline.name()); with(inline.attributes(), new Action<Map<String, String>>() { @Override public void apply(@NonNull Map<String, String> map) { assertEquals(3, map.size()); assertEquals("my-class", map.get("class")); assertEquals("", map.get("disabled")); assertEquals("there", map.get("@hello")); } }); } }); }
@Test public void blockTagNewLine() { // we should make sure that a block tag will have a new line for it's // content (white spaces before should be ignored) final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final String html = "<ul>" + " <li>ul-first" + " <li>ul-second" + " <ol>" + " <li>ol-first" + " <li>ol-second" + " </ol>" + " <li>ul-third" + "</ul>"; final StringBuilder output = new StringBuilder(); impl.processFragment(output, html); final String[] split = output.toString().split("\n"); assertEquals(Arrays.toString(split), 5, split.length); }
protected <T extends Appendable & CharSequence> void ensureNewLineIfPreviousWasBlock(@NonNull T output) { if (previousIsBlock) { ensureNewLine(output); previousIsBlock = false; } }
protected <T extends Appendable & CharSequence> void processCharacter( @NonNull T output, @NonNull Token.Character character) { // there are tags: BUTTON, INPUT, SELECT, SCRIPT, TEXTAREA, STYLE // that might have character data that we do not want to display if (isInsidePreTag) { appendQuietly(output, character.getData()); } else { ensureNewLineIfPreviousWasBlock(output); trimmingAppender.append(output, character.getData()); } }
@Test public void flushClearsInternalState() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<p><i>italic <b>bold italic</b></i></p><p>paragraph</p><div>and a div</div>"); final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction(); final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction(); impl.flushInlineTags(output.length(), inlineTagsAction); impl.flushBlockTags(output.length(), blockTagsAction); assertTrue(inlineTagsAction.called); assertTrue(blockTagsAction.called); assertEquals(2, inlineTagsAction.tags.size()); assertEquals(3, blockTagsAction.tags.size()); final CaptureInlineTagsAction captureInlineTagsAction = new CaptureInlineTagsAction(); final CaptureBlockTagsAction captureBlockTagsAction = new CaptureBlockTagsAction(); impl.flushInlineTags(output.length(), captureInlineTagsAction); impl.flushBlockTags(output.length(), captureBlockTagsAction); assertTrue(captureInlineTagsAction.called); assertTrue(captureBlockTagsAction.called); assertEquals(0, captureInlineTagsAction.tags.size()); assertEquals(0, captureBlockTagsAction.tags.size()); }
@Test public void blockCloseClosesChildren() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, html); impl.flushBlockTags(output.length(), action);
@Test public void multipleFragmentsContinuation() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(new HtmlEmptyTagReplacement()); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<i>"); output.append("italic "); impl.processFragment(output, "</i>"); final CaptureInlineTagsAction action = new CaptureInlineTagsAction(); impl.flushInlineTags(output.length(), action); assertTrue(action.called); final List<HtmlTag.Inline> inlines = action.tags; assertEquals(inlines.toString(), 1, inlines.size()); final HtmlTag.Inline inline = inlines.get(0); assertEquals("i", inline.name()); assertEquals(0, inline.start()); assertEquals(output.length(), inline.end()); assertEquals("italic ", output.toString()); }
@Test public void newLineAfterBlockTag() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); final String[] fragments = { "<h1>head #1</h1>just text", "<h2>head #2</h2><span>in span tag</span>", "<h3>head #3</h3><custom-tag>in custom-tag</custom-tag>" }; for (String fragment: fragments) { impl.processFragment(output, fragment); } final String expected = "" + "head #1\njust text\n" + "head #2\nin span tag\n" + "head #3\nin custom-tag"; assertEquals(expected, output.toString()); }
@Test public void flushCloseTagsIfRequested() { final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(); final StringBuilder output = new StringBuilder(); impl.processFragment(output, "<div><i><b><em><strong>divibemstrong"); final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction(); impl.flushInlineTags(end, inlineTagsAction); impl.flushBlockTags(end, blockTagsAction);
); final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(new HtmlEmptyTagReplacement() { @Nullable @Override impl.processFragment(output, html.toString()); impl.flushBlockTags(output.length(), action);
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create(new HtmlEmptyTagReplacement() { @Nullable @Override impl.processFragment(output, html.toString()); impl.flushInlineTags(output.length(), action);
try { htmlParser = ru.noties.markwon.html.impl.MarkwonHtmlParserImpl.create(); } catch (Throwable t) { htmlParser = MarkwonHtmlParser.noOp();