flexmark-java
是java
版的Markdown
轉換工具,基本支持Markdown
所有的語法,而且擴展性也不錯;本文主要是通過擴展形式給鏈接添加target
屬性- 本文的擴展還將支持
Spring Properties
來動態配置, 支持域名排除、支持相對路徑排除、支持自定義target
屬性的值.
定義Properties
配置類 LinkTargetProperties
@Configuration
@ConfigurationProperties(prefix = "markdown.link")
public class LinkTargetProperties{
/**
* 排除添加 target 屬性的鏈接
*/
private List<String> excludes;
/**
* target 屬性的值
*/
private String target = "_target";
/**
* 相對路徑排除
*/
private boolean relativeExclude = true;
// get 和 set 方法省略
}
實現 AttributeProvider
的類 LinkTargetAttributeProvider
擴展
flexmark-java
主要是通過實現AttributeProvider
進行修改
public class LinkTargetAttributeProvider implements AttributeProvider {
// 用於獲取配置的數據
private final DataHolder dataHolder;
// 絕對路徑正則匹配
private final Pattern pattern = Pattern.compile("^[a-zA-z]+://[^\\s]*");
public LinkTargetAttributeProvider(DataHolder dataHolder) {
this.dataHolder = dataHolder;
}
@Override
public void setAttributes(@NotNull Node node, @NotNull AttributablePart part, @NotNull Attributes attributes) {
// 只處理 Link
if (node instanceof Link && part == AttributablePart.LINK) {
// 獲取 href 標籤
Attribute hrefAttr = attributes.get("href");
if (hrefAttr == null) {
return;
}
// 值也不能爲空
String href = hrefAttr.getValue();
if (StringUtils.isEmpty(href)) {
return;
}
// 獲取配置參數
// 注意此處不能直接使用 Spring Boot 的依賴注入
// 但可以使用ApplicatonContext.getBean的形式獲取
LinkTargetProperties dataKey = FlexmarkExtensions.LINK_TARGET.get(this.dataHolder);
// 判斷是否是絕對路徑
if (!pattern.matcher(href).matches()) {
if (dataKey.isRelativeExclude()) {
// 如果是相對路徑,則排除
return;
}
} else {
// 獲取域名/host
Optional<String> host = ServletUtil.getHost(href);
if (host.isEmpty()) {
return;
}
List<String> excludes = dataKey.getExcludes();
if (excludes != null && !excludes.isEmpty()) {
// 如果包含當前的host則排除
if (excludes.contains(host.get())) {
return;
}
}
}
String target = dataKey.getTarget();
if (StringUtils.isEmpty(target)) {
target = "";
}
// 設置target 屬性
attributes.replaceValue("target", target);
}
}
static AttributeProviderFactory factory() {
return new IndependentAttributeProviderFactory() {
@Override
public @NotNull AttributeProvider apply(@NotNull LinkResolverContext linkResolverContext) {
// 在此處獲取dataHolder
return new LinkTargetAttributeProvider(linkResolverContext.getOptions());
}
};
}
}
在就版本中也可以通過
DataHoler.get(DataKey)
獲取配置參數,現在已經不推薦使用了,這裏推薦使用linkResolverContext.getOptions()
的形式
註冊 Provider
自定義的
Provider
需要通過HtmlRenderer.Builder.attributeProviderFactory
的方式註冊才能使用
public class LinkTargetExtensions implements HtmlRenderer.HtmlRendererExtension {
// 定義配置參數
// 並設置默認值
public static final DataKey<LinkTargetProperties> LINK_TARGET = new DataKey<>("LINK_TARGET", new LinkTargetProperties());
@Override
public void rendererOptions(@NotNull MutableDataHolder mutableDataHolder) {
}
@Override
public void extend(HtmlRenderer.@NotNull Builder builder, @NotNull String s) {
builder.attributeProviderFactory(LinkTargetAttributeProvider.factory());
}
public static LinkTargetExtensions create() {
return new LinkTargetExtensions();
}
}
Markdown
工具類
public class MarkdownUtil{
private static final MutableDataSet OPTIONS = new MutableDataSet(
PegdownOptionsAdapter.flexmarkOptions(
true,
// 所有的特性
Extensions.ALL,
// 自定義的 Link Target 擴展
LinkTargetExtensions.create()
))
.set(HtmlRenderer.SOFT_BREAK, "<br/>");
// 解析器
private static final Parser PARSER = Parser.builder(OPTIONS).build();
// 渲染器
private static final HtmlRenderer htmlRender = HtmlRenderer.builder(OPTIONS).build();
/**
* 渲染 Markdown
* @param markdown 文檔
* @param JDK8的Consumer的,用於動態改變 LinkTargetAttributeProvider的配置參數
* @return html
*/
public static String renderHtml(String markdown, Consumer<Document> accept) {
if (Util.isEmpty(markdown)) {
return "";
}
Document document = PARSER.parse(markdown);
if (accept != null) {
accept.accept(document);
}
return htmlRender.render(document);
}
}
使用示例
public class TestMarkdownRender(){
public static void main(String[] args) {
String markdown = "[測試1](http://www.itlangzi.com/test1 '測試1') [測試2](/test2 '測試2') [測試3](https://www.google.com/test3 '測試3')";
System.out.println(MarkdownUtil.renderHtml(markdown, doc -> {
doc.set(LinkTargetExtensions.LINK_TARGET, new LinkTargetProperties(
// 需要過濾的域名
Arrays.asList("www.itlangzi.com","www.baidu.com"),
// target 屬性的值
"_target",
// 排除相對路徑
true
));
}));
}
}
結果
碼磚不易,多多關注