All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vladsch.flexmark.html.renderer.HeaderIdGenerator Maven / Gradle / Ivy

package com.vladsch.flexmark.html.renderer;

import com.vladsch.flexmark.ast.AnchorRefTarget;
import com.vladsch.flexmark.ast.util.AnchorRefTargetBlockPreVisitor;
import com.vladsch.flexmark.ast.util.AnchorRefTargetBlockVisitor;
import com.vladsch.flexmark.html.Disposable;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.DataHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;

public class HeaderIdGenerator implements HtmlIdGenerator, Disposable {
    HashMap headerBaseIds = new HashMap<>();
    boolean resolveDupes;
    String toDashChars;
    String nonDashChars;
    boolean noDupedDashes;
    boolean nonAsciiToLowercase;

    public HeaderIdGenerator() {
        this(null);
    }

    public HeaderIdGenerator(@Nullable DataHolder options) {
        resolveDupes = HtmlRenderer.HEADER_ID_GENERATOR_RESOLVE_DUPES.get(options);
        toDashChars = HtmlRenderer.HEADER_ID_GENERATOR_TO_DASH_CHARS.get(options);
        nonDashChars = HtmlRenderer.HEADER_ID_GENERATOR_NON_DASH_CHARS.get(options);
        noDupedDashes = HtmlRenderer.HEADER_ID_GENERATOR_NO_DUPED_DASHES.get(options);
        nonAsciiToLowercase = HtmlRenderer.HEADER_ID_GENERATOR_NON_ASCII_TO_LOWERCASE.get(options);
    }

    @Override
    public void dispose() {
        headerBaseIds = null;
    }

    public boolean isResolveDupes() {
        return resolveDupes;
    }

    public void setResolveDupes(boolean resolveDupes) {
        this.resolveDupes = resolveDupes;
    }

    public String getToDashChars() {
        return toDashChars;
    }

    public void setToDashChars(String toDashChars) {
        this.toDashChars = toDashChars;
    }

    public String getNonDashChars() {
        return nonDashChars;
    }

    public void setNonDashChars(String nonDashChars) {
        this.nonDashChars = nonDashChars;
    }

    public boolean isNoDupedDashes() {
        return noDupedDashes;
    }

    public void setNoDupedDashes(boolean noDupedDashes) {
        this.noDupedDashes = noDupedDashes;
    }

    public boolean isNonAsciiToLowercase() {
        return nonAsciiToLowercase;
    }

    public void setNonAsciiToLowercase(boolean nonAsciiToLowercase) {
        this.nonAsciiToLowercase = nonAsciiToLowercase;
    }

    @Override
    public void generateIds(@NotNull Document document) {
        generateIds(document, null);
    }
    
    @Override
    public void generateIds(Document document, @Nullable AnchorRefTargetBlockPreVisitor preVisitor) {
        headerBaseIds.clear();

        resolveDupes = HtmlRenderer.HEADER_ID_GENERATOR_RESOLVE_DUPES.get(document);
        toDashChars = HtmlRenderer.HEADER_ID_GENERATOR_TO_DASH_CHARS.get(document);
        nonDashChars = HtmlRenderer.HEADER_ID_GENERATOR_NON_DASH_CHARS.get(document);
        noDupedDashes = HtmlRenderer.HEADER_ID_GENERATOR_NO_DUPED_DASHES.get(document);
        nonAsciiToLowercase = HtmlRenderer.HEADER_ID_GENERATOR_NON_ASCII_TO_LOWERCASE.get(document);

        new AnchorRefTargetBlockVisitor() {
            @Override
            protected boolean preVisit(@NotNull Node node) {
                return preVisitor == null || preVisitor.preVisit(node, this);
            }

            @Override
            protected void visit(AnchorRefTarget node) {
                if (node.getAnchorRefId().isEmpty()) {
                    String text = node.getAnchorRefText();
                    String refId = null;

                    refId = generateId(text);

                    if (refId != null) {
                        node.setAnchorRefId(refId);
                    }
                }
            }
        }.visit(document);
    }

    String generateId(String text) {
        if (!text.isEmpty()) {
            String baseRefId = generateId(text, toDashChars, nonDashChars, noDupedDashes, nonAsciiToLowercase);

            if (resolveDupes) {
                if (headerBaseIds.containsKey(baseRefId)) {
                    int index = headerBaseIds.get(baseRefId);

                    index++;
                    headerBaseIds.put(baseRefId, index);
                    baseRefId += "-" + index;
                } else {
                    headerBaseIds.put(baseRefId, 0);
                }
            }

            return baseRefId;
        }
        return null;
    }

    @Nullable
    @Override
    public String getId(@NotNull Node node) {
        return node instanceof AnchorRefTarget ? ((AnchorRefTarget) node).getAnchorRefId() : null;
    }

    @Nullable
    @Override
    public String getId(@NotNull CharSequence text) {
        return generateId(text.toString());
    }

    @SuppressWarnings("WeakerAccess")
    public static String generateId(CharSequence headerText, String toDashChars, boolean noDupedDashes, boolean nonAsciiToLowercase) {
        return generateId(headerText, toDashChars, null, noDupedDashes, nonAsciiToLowercase);
    }

    @SuppressWarnings("WeakerAccess")
    public static String generateId(CharSequence headerText, String toDashChars, String nonDashChars, boolean noDupedDashes, boolean nonAsciiToLowercase) {
        int iMax = headerText.length();
        StringBuilder baseRefId = new StringBuilder(iMax);
        if (toDashChars == null) toDashChars = HtmlRenderer.HEADER_ID_GENERATOR_TO_DASH_CHARS.get(null);
        if (nonDashChars == null) nonDashChars = HtmlRenderer.HEADER_ID_GENERATOR_NON_DASH_CHARS.get(null);

        for (int i = 0; i < iMax; i++) {
            char c = headerText.charAt(i);
            if (isAlphabetic(c)) {
                if (!nonAsciiToLowercase && !(c >= 'A' && c <= 'Z')) {
                    baseRefId.append(c);
                } else {
                    baseRefId.append(Character.toLowerCase(c));
                }
            } else if (Character.isDigit(c)) baseRefId.append(c);
            else if (nonDashChars.indexOf(c) != -1) baseRefId.append(c);
            else if (toDashChars.indexOf(c) != -1 && (!noDupedDashes
                    || ((c == '-' && baseRefId.length() == 0)
                    || baseRefId.length() != 0 && baseRefId.charAt(baseRefId.length() - 1) != '-'))
            ) baseRefId.append('-');
        }
        return baseRefId.toString();
    }

    public static boolean isAlphabetic(char c) {
        return (((((1 << Character.UPPERCASE_LETTER) |
                (1 << Character.LOWERCASE_LETTER) |
                (1 << Character.TITLECASE_LETTER) |
                (1 << Character.MODIFIER_LETTER) |
                (1 << Character.OTHER_LETTER) |
                (1 << Character.LETTER_NUMBER)) >> Character.getType((int) c)) & 1) != 0);
    }

    public static class Factory implements HeaderIdGeneratorFactory {
        @NotNull
        @Override
        public HeaderIdGenerator create(@NotNull LinkResolverContext context) {
            return new HeaderIdGenerator();
        }

        @NotNull
        @Override
        public HeaderIdGenerator create() {
            return new HeaderIdGenerator();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy