View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.internal;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.stream.Collectors;
35  
36  import javax.xml.parsers.SAXParser;
37  import javax.xml.parsers.SAXParserFactory;
38  
39  import org.junit.jupiter.api.Test;
40  import org.xml.sax.Attributes;
41  import org.xml.sax.helpers.DefaultHandler;
42  
43  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
44  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
45  import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
46  
47  public class XdocsUrlTest {
48  
49      private static final String PACKAGE_NAME =
50              "src/main/java/com/puppycrawl/tools/checkstyle/checks";
51  
52      private static final String TREE_WORKER = "TreeWalker";
53  
54      private static final String SUFFIX_CHECK = "Check";
55  
56      private static final String CHECKS = "checks";
57  
58      private static final String MISC = "misc";
59  
60      private static final String ANNOTATION = "annotation";
61  
62      private static final String COMMENTS_INDENTATION = "CommentsIndentation";
63  
64      private static final String INDENTATION = "Indentation";
65  
66      private static final String SUPPRESS_WARNINGS_HOLDER = "SuppressWarningsHolder";
67  
68      private static final Path AVAILABLE_CHECKS_PATH = Path.of("src/site/xdoc/checks.xml");
69  
70      private static Map<String, List<String>> getXdocsMap() throws IOException {
71          final Map<String, List<String>> checksNamesMap = new HashMap<>();
72          final Set<Class<?>> checkSet = CheckUtil.getCheckstyleModules();
73          final Set<Class<?>> treeWalkerOrFileSetCheckSet = checkSet.stream()
74                  .filter(clazz -> {
75                      return AbstractCheck.class.isAssignableFrom(clazz)
76                              || AbstractFileSetCheck.class.isAssignableFrom(clazz);
77                  })
78                  .collect(Collectors.toUnmodifiableSet());
79          for (Class<?> check : treeWalkerOrFileSetCheckSet) {
80              final String checkName = check.getSimpleName();
81              if (!TREE_WORKER.equals(checkName)) {
82                  String packageName = check.getPackage().getName();
83                  packageName = packageName.substring(packageName.lastIndexOf('.') + 1);
84                  if (CHECKS.equals(packageName)) {
85                      packageName = MISC;
86                  }
87                  if (checksNamesMap.get(packageName) == null) {
88                      final List<String> arrayList = new ArrayList<>();
89                      arrayList.add(checkName);
90                      checksNamesMap.put(packageName, arrayList);
91                  }
92                  else {
93                      checksNamesMap.get(packageName).add(checkName);
94                  }
95              }
96          }
97          return checksNamesMap;
98      }
99  
100     @Test
101     public void testXdocsUrl() throws Exception {
102         final SAXParserFactory parserFactory = SAXParserFactory.newInstance();
103         final SAXParser parser = parserFactory.newSAXParser();
104         final DummyHandler checkHandler = new DummyHandler();
105         try (InputStream input = Files.newInputStream(AVAILABLE_CHECKS_PATH)) {
106             parser.parse(input, checkHandler);
107         }
108         final Map<String, List<String>> checksNamesMap = getXdocsMap();
109         for (List<String> sub : checkHandler.checkNamesList) {
110             final String moduleName = sub.get(0);
111             final String checkNameInAttribute = sub.get(1);
112             final String checkNameInText = sub.get(2);
113             final String checkNameInconsistentErrorMsg = String.format(Locale.ROOT,
114                     "Check with name '%s' in attribute "
115                             + "is not consistent with check name in text in file '%s'",
116                     checkNameInAttribute, AVAILABLE_CHECKS_PATH);
117             assertWithMessage(checkNameInconsistentErrorMsg)
118                     .that(checkNameInText)
119                     .matches(checkNameInAttribute);
120             final String checkNameModuleErrorMsg = String.format(Locale.ROOT,
121                     "Check with name '%s' is not in '%s' module",
122                     checkNameInAttribute, moduleName);
123             if (COMMENTS_INDENTATION.equals(checkNameInAttribute)
124                     || INDENTATION.equals(checkNameInAttribute)) {
125                 assertWithMessage(checkNameModuleErrorMsg)
126                         .that(moduleName)
127                         .matches(MISC);
128             }
129             else if (SUPPRESS_WARNINGS_HOLDER.equals(checkNameInAttribute)) {
130                 assertWithMessage(checkNameModuleErrorMsg)
131                         .that(moduleName)
132                         .matches(ANNOTATION);
133             }
134             else {
135                 final List<String> moduleFileNames = checksNamesMap.get(moduleName);
136                 final String moduleNameErrorMsg = String.format(Locale.ROOT,
137                         "module name: '%s' does not exist in '%s'", moduleName, PACKAGE_NAME);
138                 assertWithMessage(moduleNameErrorMsg)
139                         .that(moduleFileNames)
140                         .isNotNull();
141                 final String checkNameWithSuffix = checkNameInAttribute + SUFFIX_CHECK;
142                 assertWithMessage(checkNameModuleErrorMsg)
143                         .that(moduleFileNames)
144                         .contains(checkNameWithSuffix);
145             }
146         }
147     }
148 
149     public static final class DummyHandler extends DefaultHandler {
150 
151         private static final String SPLIT_CHECK_NAME_IN_ATTRIBUTE = "#";
152 
153         private static final String PREFIX_CONFIG = "config_";
154 
155         private static final String NODE_NAME = "a";
156 
157         private final List<List<String>> checkNamesList = new ArrayList<>();
158 
159         private List<String> singleCheckNameList;
160 
161         private String currentTag;
162 
163         @Override
164         public void startElement(String uri, String localName, String qName,
165                                  Attributes attributes) {
166             if (NODE_NAME.equals(qName)) {
167                 final String[] moduleAndCheckName =
168                         attributes.getValue(0).split(SPLIT_CHECK_NAME_IN_ATTRIBUTE);
169                 if (moduleAndCheckName[0].startsWith(PREFIX_CONFIG)) {
170                     singleCheckNameList = new ArrayList<>();
171                     final String moduleName =
172                             moduleAndCheckName[0].replaceAll("(.*config_)|(\\.html.*)", "");
173                     singleCheckNameList.add(moduleName);
174                     singleCheckNameList.add(moduleAndCheckName[1]);
175                 }
176             }
177             currentTag = qName;
178         }
179 
180         @Override
181         public void characters(char[] ch, int start, int length) {
182             if (currentTag != null && singleCheckNameList != null && currentTag.equals(NODE_NAME)) {
183                 final String currentValue = new String(ch, start, length).trim();
184                 if (!currentValue.isEmpty() && !"\n".equals(currentValue)) {
185                     singleCheckNameList.add(currentValue);
186                 }
187                 currentTag = null;
188             }
189         }
190 
191         @Override
192         public void endElement(String uri, String localName, String qName) {
193             if (singleCheckNameList != null && qName.equals(NODE_NAME)) {
194                 checkNamesList.add(singleCheckNameList);
195                 singleCheckNameList = null;
196             }
197         }
198     }
199 }