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