View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.site;
21  
22  import java.net.URLEncoder;
23  import java.nio.charset.StandardCharsets;
24  import java.util.Set;
25  
26  import org.apache.maven.doxia.macro.AbstractMacro;
27  import org.apache.maven.doxia.macro.Macro;
28  import org.apache.maven.doxia.macro.MacroExecutionException;
29  import org.apache.maven.doxia.macro.MacroRequest;
30  import org.apache.maven.doxia.module.xdoc.XdocSink;
31  import org.apache.maven.doxia.sink.Sink;
32  import org.codehaus.plexus.component.annotations.Component;
33  
34  /**
35   * A macro that inserts a list of the violation messages.
36   */
37  @Component(role = Macro.class, hint = "violation-messages")
38  public class ViolationMessagesMacro extends AbstractMacro {
39      @Override
40      public void execute(Sink sink, MacroRequest request) throws MacroExecutionException {
41          // until https://github.com/checkstyle/checkstyle/issues/13426
42          if (!(sink instanceof XdocSink xdocSink)) {
43              throw new MacroExecutionException("Expected Sink to be an XdocSink.");
44          }
45          final String checkName = (String) request.getParameter("checkName");
46          final Object instance = SiteUtil.getModuleInstance(checkName);
47          final Class<?> clss = instance.getClass();
48          final Set<String> messageKeys = SiteUtil.getMessageKeys(clss);
49          createListOfMessages(xdocSink, clss, messageKeys);
50      }
51  
52      /**
53       * Iterates through the fields of the class and creates an unordered list.
54       *
55       * @param sink the sink to write to.
56       * @param clss the class of the fields.
57       * @param messageKeys the List of message keys to iterate through.
58       */
59      private static void createListOfMessages(
60              XdocSink sink, Class<?> clss, Set<String> messageKeys) {
61          final String indentLevel8 = SiteUtil.getNewlineAndIndentSpaces(8);
62  
63          // This is a hack to prevent a newline from being inserted by the default sink.
64          // Once we get rid of the custom parser, we can remove this.
65          // until https://github.com/checkstyle/checkstyle/issues/13426
66          sink.setInsertNewline(false);
67          sink.list();
68          sink.setInsertNewline(true);
69  
70          for (String messageKey : messageKeys) {
71              createListItem(sink, clss, messageKey);
72          }
73          sink.rawText(indentLevel8);
74          sink.list_();
75      }
76  
77      /**
78       * Creates a list item for the given field.
79       *
80       * @param sink the sink to write to.
81       * @param clss the class of the field.
82       * @param messageKey the message key.
83       */
84      private static void createListItem(XdocSink sink, Class<?> clss, String messageKey) {
85          final String messageKeyUrl = constructMessageKeyUrl(clss, messageKey);
86          final String indentLevel10 = SiteUtil.getNewlineAndIndentSpaces(10);
87          final String indentLevel12 = SiteUtil.getNewlineAndIndentSpaces(12);
88          final String indentLevel14 = SiteUtil.getNewlineAndIndentSpaces(14);
89          // Place the <li>.
90          sink.rawText(indentLevel10);
91          // This is a hack to prevent a newline from being inserted by the default sink.
92          // Once we get rid of the custom parser, we can remove this.
93          // until https://github.com/checkstyle/checkstyle/issues/13426
94          sink.setInsertNewline(false);
95          sink.listItem();
96          sink.setInsertNewline(true);
97  
98          // Place an <a>.
99          sink.rawText(indentLevel12);
100         sink.link(messageKeyUrl);
101         // Further indent the text.
102         sink.rawText(indentLevel14);
103         sink.rawText(messageKey);
104 
105         // Place closing </a> and </li> tags.
106         sink.rawText(indentLevel12);
107         sink.link_();
108         sink.rawText(indentLevel10);
109         sink.listItem_();
110     }
111 
112     /**
113      * Constructs a URL to GitHub that searches for the message key.
114      *
115      * @param clss the class of the module.
116      * @param messageKey the message key.
117      * @return the URL to GitHub.
118      */
119     private static String constructMessageKeyUrl(Class<?> clss, String messageKey) {
120         final String query = "path:src/main/resources/"
121                 + clss.getPackage().getName().replace('.', '/')
122                 + " path:**/messages*.properties repo:checkstyle/checkstyle \""
123                 + messageKey + "\"";
124 
125         return "https://github.com/search?q="
126                 + URLEncoder.encode(query, StandardCharsets.UTF_8);
127     }
128 }