1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.internal.utils;
21
22 import java.io.IOException;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
34
35 import javax.xml.parsers.DocumentBuilder;
36 import javax.xml.parsers.DocumentBuilderFactory;
37 import javax.xml.parsers.ParserConfigurationException;
38
39 import org.w3c.dom.Document;
40 import org.w3c.dom.Element;
41 import org.w3c.dom.Node;
42 import org.w3c.dom.NodeList;
43 import org.xml.sax.SAXException;
44
45
46
47
48
49
50
51 public final class XdocUtil {
52
53 public static final String DIRECTORY_PATH = "src/site/xdoc";
54
55 private XdocUtil() {
56 }
57
58
59
60
61
62
63
64 public static Set<Path> getXdocsFilePaths() throws IOException {
65 final Path directory = Path.of(DIRECTORY_PATH);
66 try (Stream<Path> stream = Files.find(directory, Integer.MAX_VALUE,
67 (path, attr) -> {
68 return attr.isRegularFile()
69 && (path.toString().endsWith(".xml")
70 || path.toString().endsWith(".xml.vm"));
71 })) {
72 return stream.collect(Collectors.toUnmodifiableSet());
73 }
74 }
75
76
77
78
79
80
81
82
83
84 public static Set<Path> getXdocsTemplatesFilePaths() throws IOException {
85 final Path directory = Path.of(DIRECTORY_PATH);
86 try (Stream<Path> stream = Files.find(directory, Integer.MAX_VALUE,
87 (path, attr) -> {
88 return attr.isRegularFile()
89 && path.toString().endsWith(".xml.template");
90 })) {
91 return stream.collect(Collectors.toUnmodifiableSet());
92 }
93 }
94
95
96
97
98
99
100
101 public static Set<Path> getXdocsConfigFilePaths(Set<Path> files) {
102 final Set<Path> xdocs = new HashSet<>();
103 for (Path entry : files) {
104 final String fileName = entry.getFileName().toString();
105 if (!entry.getParent().toString().matches("src[\\\\/]site[\\\\/]xdocs")
106 && fileName.endsWith(".xml")) {
107 xdocs.add(entry);
108 }
109 }
110 return xdocs;
111 }
112
113
114
115
116
117
118
119 public static Set<Path> getXdocsStyleFilePaths(Set<Path> files) {
120 final Set<Path> xdocs = new HashSet<>();
121 for (Path entry : files) {
122 final String fileName = entry.getFileName().toString();
123 if (fileName.endsWith("_style.xml")) {
124 xdocs.add(entry);
125 }
126 }
127 return xdocs;
128 }
129
130
131
132
133
134
135
136
137
138
139 public static Set<String> getModulesNamesWhichHaveXdoc() throws Exception {
140 final DocumentBuilderFactory factory = DocumentBuilderFactory
141 .newInstance();
142
143
144
145 factory.setNamespaceAware(false);
146 factory.setValidating(false);
147 factory.setFeature("http://xml.org/sax/features/namespaces", false);
148 factory.setFeature("http://xml.org/sax/features/validation", false);
149 factory.setFeature(
150 "http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
151 false);
152 factory.setFeature(
153 "http://apache.org/xml/features/nonvalidating/load-external-dtd",
154 false);
155
156 final Set<String> modulesNamesWhichHaveXdoc = new HashSet<>();
157
158 for (Path path : getXdocsConfigFilePaths(getXdocsFilePaths())) {
159 final DocumentBuilder builder = factory.newDocumentBuilder();
160 final Document document = builder.parse(path.toFile());
161
162
163
164
165
166 document.getDocumentElement().normalize();
167
168 final NodeList nodeList = document.getElementsByTagName("section");
169
170 for (int i = 0; i < nodeList.getLength(); i++) {
171 final Node currentNode = nodeList.item(i);
172 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
173 final Element module = (Element) currentNode;
174 final String moduleName = module.getAttribute("name");
175 if (!"Content".equals(moduleName)
176 && !"Overview".equals(moduleName)) {
177 modulesNamesWhichHaveXdoc.add(moduleName);
178 }
179 }
180 }
181 }
182 return modulesNamesWhichHaveXdoc;
183 }
184
185
186
187
188
189
190
191 public static Map<String, Set<String>> extractUsedPropertiesFromXdocsExamples()
192 throws IOException {
193 final List<Path> roots = List.of(
194 Path.of("src/xdocs-examples/resources/com/puppycrawl/tools/checkstyle/checks"),
195 Path.of("src/xdocs-examples/resources-noncompilable/"
196 + "com/puppycrawl/tools/checkstyle/checks")
197 );
198
199 final Map<String, Set<String>> checkToProperties = new HashMap<>();
200
201 for (Path root : roots) {
202 if (Files.exists(root)) {
203 try (Stream<Path> paths = Files.walk(root)) {
204 paths.filter(path -> path.toString().endsWith(".java"))
205 .forEach(path -> processXdocExampleFile(path, checkToProperties));
206 }
207 }
208 }
209
210 return checkToProperties;
211 }
212
213 private static void processXdocExampleFile(
214 Path file, Map<String, Set<String>> checkToProperties) {
215 try {
216 final String content = Files.readString(file);
217 final Matcher xmlBlockMatcher =
218 Pattern.compile("/\\*xml(.*?)\\*/", Pattern.DOTALL).matcher(content);
219
220 if (xmlBlockMatcher.find()) {
221 final Map.Entry<String, Set<String>> entry =
222 parseConfigBlock(xmlBlockMatcher.group(1));
223
224 if (entry != null) {
225 final String checkClassKey = entry.getKey();
226 final Set<String> props = entry.getValue();
227 checkToProperties
228 .computeIfAbsent(checkClassKey, key -> new HashSet<>())
229 .addAll(props);
230
231
232 if (checkClassKey.endsWith("Check")) {
233 final String moduleNameKey = checkClassKey.substring(
234 0, checkClassKey.length() - "Check".length());
235 checkToProperties
236 .computeIfAbsent(moduleNameKey, key -> new HashSet<>())
237 .addAll(props);
238 }
239 }
240 }
241 }
242 catch (IOException ioe) {
243 throw new IllegalStateException("Error reading file: " + file, ioe);
244 }
245 }
246
247 private static Map.Entry<String, Set<String>> parseConfigBlock(String configBlock) {
248 final Matcher propMatcher =
249 Pattern.compile("<property name=\"([^\"]+)\"").matcher(configBlock);
250 final Set<String> props = new HashSet<>();
251 int firstPropStart = -1;
252 while (propMatcher.find()) {
253 if (firstPropStart == -1) {
254 firstPropStart = propMatcher.start();
255 }
256 props.add(propMatcher.group(1));
257 }
258
259 Map.Entry<String, Set<String>> result = null;
260 if (!props.isEmpty()) {
261
262
263
264 final Matcher moduleMatcher =
265 Pattern.compile("<module name=\"([^\"]+)\"")
266 .matcher(configBlock.substring(0, firstPropStart));
267 String lastModule = null;
268 while (moduleMatcher.find()) {
269 lastModule = moduleMatcher.group(1);
270 }
271
272 if (lastModule != null) {
273 final String checkClassName;
274 if (lastModule.endsWith("Check")) {
275 checkClassName = lastModule;
276 }
277 else {
278 checkClassName = lastModule + "Check";
279 }
280 result = Map.entry(checkClassName, props);
281 }
282 }
283
284 return result;
285 }
286 }