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     * @param context a {@link com.vladsch.flexmark.html.renderer.LinkResolverContext} object.
063     */
064    public FlexmarkDoxiaLinkResolver(@NotNull LinkResolverBasicContext context) {
065        this.pattern = Pattern.compile("^(?![^:]+:)((?:\\./)?(?:\\.\\./)*[^\\.]+).(?:"
066                + MarkdownParserModule.FILE_EXTENSION
067                + "|"
068                + MarkdownParserModule.ALTERNATE_FILE_EXTENSION
069                + ")(#[^#\\.]*){0,1}$");
070    }
071
072    /** {@inheritDoc} */
073    @Override
074    public @NotNull ResolvedLink resolveLink(
075            @NotNull Node node, @NotNull LinkResolverBasicContext context, @NotNull ResolvedLink link) {
076        if (link.getLinkType() == LinkType.LINK) {
077            Matcher matcher = this.pattern.matcher(link.getUrl());
078            if (matcher.matches()) {
079                return link.withStatus(LinkStatus.VALID).withUrl(matcher.replaceAll("$1.html$2"));
080            }
081        }
082
083        return link;
084    }
085
086    /**
087     * Factory that creates FlexmarkDoxiaLinkResolver objects.
088     */
089    public static class Factory extends IndependentLinkResolverFactory {
090        @Override
091        public @Nullable Set<Class<?>> getBeforeDependents() {
092            Set<Class<?>> set = new HashSet<>();
093            set.add(WikiLinkLinkResolver.Factory.class);
094            return set;
095        }
096
097        @Override
098        public @NotNull LinkResolver apply(@NotNull LinkResolverBasicContext context) {
099            return new FlexmarkDoxiaLinkResolver(context);
100        }
101    }
102}