-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
221 additions
and
2 deletions.
There are no files selected for viewing
164 changes: 164 additions & 0 deletions
164
src/main/java/org/segrada/rendering/markdown/MarkdownRenderer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package org.segrada.rendering.markdown; | ||
|
||
import com.google.inject.Injector; | ||
import com.vladsch.flexmark.ext.emoji.EmojiExtension; | ||
import com.vladsch.flexmark.ext.tables.TablesExtension; | ||
import com.vladsch.flexmark.ext.typographic.TypographicExtension; | ||
import com.vladsch.flexmark.ext.wikilink.WikiLink; | ||
import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkRefProcessor; | ||
import com.vladsch.flexmark.html.HtmlRenderer; | ||
import com.vladsch.flexmark.html.HtmlRenderer.Builder; | ||
import com.vladsch.flexmark.html.HtmlWriter; | ||
import com.vladsch.flexmark.html.renderer.*; | ||
import com.vladsch.flexmark.parser.Parser; | ||
import com.vladsch.flexmark.util.ast.Node; | ||
import com.vladsch.flexmark.util.ast.TextCollectingVisitor; | ||
import com.vladsch.flexmark.util.data.DataHolder; | ||
import com.vladsch.flexmark.util.data.MutableDataHolder; | ||
import com.vladsch.flexmark.util.data.MutableDataSet; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
import org.segrada.model.prototype.ISource; | ||
import org.segrada.service.SourceService; | ||
import org.unbescape.html.HtmlEscape; | ||
|
||
import java.util.*; | ||
|
||
public class MarkdownRenderer { | ||
/** | ||
* reference to injector | ||
*/ | ||
public static Injector injector; | ||
|
||
/** | ||
* cache for source reference links | ||
* TODO: use different cache, because otherwise this might fill up memory eventually, rather unlikely, but there is no deletion of old keys here | ||
*/ | ||
private static final Map<String, String> sourceReferenceCache = new HashMap<>(); | ||
|
||
public static void setInjector(Injector injector) { | ||
MarkdownRenderer.injector = injector; | ||
} | ||
|
||
/** | ||
* get current source service instance from injector | ||
* @return SourceService instance | ||
*/ | ||
public static SourceService getSourceService() { | ||
return injector.getInstance(SourceService.class); | ||
} | ||
|
||
// markdown data holder with custom extension + more extensions defined below | ||
final private static DataHolder OPTIONS = new MutableDataSet() | ||
.set(TablesExtension.CLASS_NAME, "table table-condensed") | ||
.set(Parser.EXTENSIONS, Arrays.asList( | ||
CustomExtension.create(), // will use wiki link renderer to work links | ||
EmojiExtension.create(), | ||
TablesExtension.create(), | ||
TypographicExtension.create() | ||
)).toImmutable(); | ||
|
||
// markdown parser and renderer | ||
static final Parser PARSER = Parser.builder(OPTIONS).build(); | ||
static final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build(); | ||
|
||
/** | ||
* Render or remove links | ||
*/ | ||
public static class BibliographicEntryLinkNodeRenderer implements NodeRenderer { | ||
@Override | ||
public @Nullable Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() { | ||
HashSet<NodeRenderingHandler<?>> set = new HashSet(); | ||
set.add(new NodeRenderingHandler(WikiLink.class, this::render)); | ||
return set; | ||
} | ||
|
||
private void render(Node node, @NotNull NodeRendererContext context, @NotNull HtmlWriter html) { | ||
if (node instanceof WikiLink) { | ||
WikiLink link = (WikiLink)node; | ||
|
||
String possibleKey = link.getLink().toString(); | ||
|
||
SourceService sourceService = getSourceService(); | ||
|
||
// fallback in case of errors | ||
if (sourceService == null) { | ||
html.text(node.getChars().unescape()); | ||
return; | ||
} | ||
|
||
// find corresponding source | ||
ISource source = sourceService.findByRef(possibleKey); | ||
if (source == null) { | ||
// not found: just print text | ||
html.text(node.getChars().unescape()); | ||
} else { | ||
// otherwise: create nice link | ||
String url = "source/show/" + source.getUid(); | ||
|
||
ResolvedLink resolvedLink = context.resolveLink(LinkType.LINK, url, (Boolean)null); | ||
html.attr("href", url); | ||
html.attr("class", "sg-data-add"); | ||
html.attr("title", source.getShortRef()); | ||
html.srcPos(node.getChars()).withAttr(resolvedLink).tag("a"); | ||
html.text(source.getShortTitle()); | ||
html.tag("/a"); | ||
} | ||
} | ||
} | ||
|
||
public static class Factory implements NodeRendererFactory { | ||
public Factory() { | ||
} | ||
|
||
@NotNull | ||
public NodeRenderer apply(@NotNull DataHolder options) { | ||
return new BibliographicEntryLinkNodeRenderer(); | ||
} | ||
} | ||
} | ||
|
||
static class CustomExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension { | ||
@Override | ||
public void rendererOptions(@NotNull MutableDataHolder options) { | ||
|
||
} | ||
|
||
@Override | ||
public void parserOptions(MutableDataHolder mutableDataHolder) { | ||
} | ||
|
||
@Override | ||
public void extend(Parser.Builder parserBuilder) { | ||
parserBuilder.linkRefProcessorFactory(new WikiLinkLinkRefProcessor.Factory()); // we will use wiki link extension to help us build links | ||
} | ||
|
||
@Override | ||
public void extend(@NotNull Builder htmlRendererBuilder, @NotNull String rendererType) { | ||
htmlRendererBuilder.nodeRendererFactory(new BibliographicEntryLinkNodeRenderer.Factory()); | ||
} | ||
|
||
static CustomExtension create() { | ||
return new CustomExtension(); | ||
} | ||
} | ||
|
||
/** | ||
* renders markdown to HTML | ||
* @param markdown source | ||
* @return html | ||
*/ | ||
public static String render(String markdown) { | ||
return RENDERER.render(PARSER.parse(markdown)); | ||
} | ||
|
||
/** | ||
* converts markdown to plain text | ||
* @param markdown source | ||
* @return text | ||
*/ | ||
public static String toPlainText(String markdown) { | ||
TextCollectingVisitor textCollectingVisitor = new TextCollectingVisitor(); | ||
return textCollectingVisitor.collectAndGetText(PARSER.parse(markdown)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/main/java/org/segrada/rendering/markup/MarkdownMarkupFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.segrada.rendering.markup; | ||
|
||
import org.jsoup.Jsoup; | ||
import org.jsoup.parser.Parser; | ||
import org.jsoup.safety.Whitelist; | ||
import org.segrada.rendering.markdown.MarkdownRenderer; | ||
|
||
/** | ||
* Copyright 2015-2021 Maximilian Kalus [segrada@auxnet.de] | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* Markdown markup filter | ||
*/ | ||
public class MarkdownMarkupFilter extends MarkupFilter { | ||
@Override | ||
public String toHTML(String markupText) { | ||
return MarkdownRenderer.render(markupText); | ||
} | ||
|
||
@Override | ||
public String toPlain(String markupText) { | ||
// sane default | ||
if (markupText == null || markupText.equals("")) return ""; | ||
|
||
// remove bibliographic entries | ||
String plainText = markupText.replaceAll("\\[\\[[\\w:]+\\]\\]", "").replaceAll("\\[[0-9f]+:\\]", ""); | ||
|
||
// clean | ||
return MarkdownRenderer.toPlainText(plainText); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.