001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.doxia.module.markdown;
020
021import java.util.HashSet;
022import java.util.Set;
023import java.util.regex.Matcher;
024import java.util.regex.Pattern;
025
026import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkResolver;
027import com.vladsch.flexmark.html.IndependentLinkResolverFactory;
028import com.vladsch.flexmark.html.LinkResolver;
029import com.vladsch.flexmark.html.renderer.LinkResolverBasicContext;
030import com.vladsch.flexmark.html.renderer.LinkStatus;
031import com.vladsch.flexmark.html.renderer.LinkType;
032import com.vladsch.flexmark.html.renderer.ResolvedLink;
033import com.vladsch.flexmark.util.ast.Node;
034import org.jetbrains.annotations.NotNull;
035import org.jetbrains.annotations.Nullable;
036
037/**
038 * The FlexmarkDoxiaLinkResolver rewrites the md, markdown links to html.
039 *
040 * Sample links it rewrites:
041 * - doc.md to doc.html
042 * - doc.markdown to doc.html
043 * - doc.md#anchor to doc.html#anchor
044 * - doc.markdown#anchor to doc.html#anchor
045 * - ../doc.markdown#anchor to ../doc.html#anchor
046 * - :doc.md to :doc.html
047 * - :doc.markdown to :doc.html
048 *
049 * Sample links it leaves untouched:
050 * - http://doc.md
051 * - https://doc.markdown
052 * - doc.md.badformat
053 * - doc.md#bad#format
054 * - doc.md#bad.format
055 */
056public class FlexmarkDoxiaLinkResolver implements LinkResolver {
057    final Pattern pattern;
058
059    /**
060     * <p>Constructor for FlexmarkDoxiaLinkResolver.</p>
061     *
062     */
063    public FlexmarkDoxiaLinkResolver() {
064        this.pattern = Pattern.compile("^(?![^:]+:)((?:\\./)?(?:\\.\\./)*[^\\.]+).(?:"
065                + MarkdownParserModule.FILE_EXTENSION
066                + "|"
067                + MarkdownParserModule.ALTERNATE_FILE_EXTENSION
068                + ")(#[^#\\.]*){0,1}$");
069    }
070
071    @Override
072    public @NotNull ResolvedLink resolveLink(
073            @NotNull Node node, @NotNull LinkResolverBasicContext context, @NotNull ResolvedLink link) {
074        if (link.getLinkType() == LinkType.LINK) {
075            Matcher matcher = this.pattern.matcher(link.getUrl());
076            if (matcher.matches()) {
077                return link.withStatus(LinkStatus.VALID).withUrl(matcher.replaceAll("$1.html$2"));
078            }
079        }
080
081        return link;
082    }
083
084    /**
085     * Factory that creates FlexmarkDoxiaLinkResolver objects.
086     */
087    public static class Factory extends IndependentLinkResolverFactory {
088        @Override
089        public @Nullable Set<Class<?>> getBeforeDependents() {
090            Set<Class<?>> set = new HashSet<>();
091            set.add(WikiLinkLinkResolver.Factory.class);
092            return set;
093        }
094
095        @Override
096        public @NotNull LinkResolver apply(@NotNull LinkResolverBasicContext context) {
097            return new FlexmarkDoxiaLinkResolver();
098        }
099    }
100}