001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.site;
021
022import java.util.Set;
023
024import org.apache.maven.doxia.macro.AbstractMacro;
025import org.apache.maven.doxia.macro.Macro;
026import org.apache.maven.doxia.macro.MacroExecutionException;
027import org.apache.maven.doxia.macro.MacroRequest;
028import org.apache.maven.doxia.module.xdoc.XdocSink;
029import org.apache.maven.doxia.sink.Sink;
030import org.codehaus.plexus.component.annotations.Component;
031
032/**
033 * A macro that inserts a list of the violation messages.
034 */
035@Component(role = Macro.class, hint = "violation-messages")
036public class ViolationMessagesMacro extends AbstractMacro {
037    @Override
038    public void execute(Sink sink, MacroRequest request) throws MacroExecutionException {
039        // until https://github.com/checkstyle/checkstyle/issues/13426
040        if (!(sink instanceof XdocSink)) {
041            throw new MacroExecutionException("Expected Sink to be an XdocSink.");
042        }
043        final String checkName = (String) request.getParameter("checkName");
044        final Object instance = SiteUtil.getModuleInstance(checkName);
045        final Class<?> clss = instance.getClass();
046        final Set<String> messageKeys = SiteUtil.getMessageKeys(clss);
047        createListOfMessages((XdocSink) sink, clss, messageKeys);
048    }
049
050    /**
051     * Iterates through the fields of the class and creates an unordered list.
052     *
053     * @param sink the sink to write to.
054     * @param clss the class of the fields.
055     * @param messageKeys the List of message keys to iterate through.
056     */
057    private static void createListOfMessages(
058            XdocSink sink, Class<?> clss, Set<String> messageKeys) {
059        final String indentLevel8 = SiteUtil.getNewlineAndIndentSpaces(8);
060
061        // This is a hack to prevent a newline from being inserted by the default sink.
062        // Once we get rid of the custom parser, we can remove this.
063        // until https://github.com/checkstyle/checkstyle/issues/13426
064        sink.setInsertNewline(false);
065        sink.list();
066        sink.setInsertNewline(true);
067
068        for (String messageKey : messageKeys) {
069            createListItem(sink, clss, messageKey);
070        }
071        sink.rawText(indentLevel8);
072        sink.list_();
073    }
074
075    /**
076     * Creates a list item for the given field.
077     *
078     * @param sink the sink to write to.
079     * @param clss the class of the field.
080     * @param messageKey the message key.
081     */
082    private static void createListItem(XdocSink sink, Class<?> clss, String messageKey) {
083        final String messageKeyUrl = constructMessageKeyUrl(clss, messageKey);
084        final String indentLevel10 = SiteUtil.getNewlineAndIndentSpaces(10);
085        final String indentLevel12 = SiteUtil.getNewlineAndIndentSpaces(12);
086        final String indentLevel14 = SiteUtil.getNewlineAndIndentSpaces(14);
087        // Place the <li>.
088        sink.rawText(indentLevel10);
089        // This is a hack to prevent a newline from being inserted by the default sink.
090        // Once we get rid of the custom parser, we can remove this.
091        // until https://github.com/checkstyle/checkstyle/issues/13426
092        sink.setInsertNewline(false);
093        sink.listItem();
094        sink.setInsertNewline(true);
095
096        // Place an <a>.
097        sink.rawText(indentLevel12);
098        sink.link(messageKeyUrl);
099        // Further indent the text.
100        sink.rawText(indentLevel14);
101        sink.rawText(messageKey);
102
103        // Place closing </a> and </li> tags.
104        sink.rawText(indentLevel12);
105        sink.link_();
106        sink.rawText(indentLevel10);
107        sink.listItem_();
108    }
109
110    /**
111     * Constructs a URL to GitHub that searches for the message key.
112     *
113     * @param clss the class of the module.
114     * @param messageKey the message key.
115     * @return the URL to GitHub.
116     */
117    private static String constructMessageKeyUrl(Class<?> clss, String messageKey) {
118        return "https://github.com/search?q="
119                + "path%3Asrc%2Fmain%2Fresources%2F"
120                + clss.getPackage().getName().replace(".", "%2F")
121                + "%20path%3A**%2Fmessages*.properties+repo%3Acheckstyle%2F"
122                + "checkstyle+%22" + messageKey + "%22";
123    }
124}