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.utils;
21  
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.nio.file.Paths;
26  import java.util.HashSet;
27  import java.util.Set;
28  import java.util.stream.Collectors;
29  import java.util.stream.Stream;
30  
31  import javax.xml.parsers.DocumentBuilder;
32  import javax.xml.parsers.DocumentBuilderFactory;
33  import javax.xml.parsers.ParserConfigurationException;
34  
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Node;
38  import org.w3c.dom.NodeList;
39  import org.xml.sax.SAXException;
40  
41  /**
42   * XdocUtil.
43   *
44   * @noinspection ClassOnlyUsedInOnePackage
45   * @noinspectionreason ClassOnlyUsedInOnePackage - class is internal tool, and only used in testing
46   */
47  public final class XdocUtil {
48  
49      public static final String DIRECTORY_PATH = "src/xdocs";
50  
51      private XdocUtil() {
52      }
53  
54      /**
55       * Gets xdocs file paths.
56       *
57       * @return a set of xdocs file paths.
58       * @throws IOException if an I/O error occurs.
59       */
60      public static Set<Path> getXdocsFilePaths() throws IOException {
61          final Path directory = Paths.get(DIRECTORY_PATH);
62          try (Stream<Path> stream = Files.find(directory, Integer.MAX_VALUE,
63                  (path, attr) -> {
64                      return attr.isRegularFile()
65                              && (path.toString().endsWith(".xml")
66                              || path.toString().endsWith(".xml.vm"));
67                  })) {
68              return stream.collect(Collectors.toUnmodifiableSet());
69          }
70      }
71  
72      /**
73       * Gets xdocs template file paths. These are files ending with .xml.template.
74       * This module will be removed once
75       * <a href="https://github.com/checkstyle/checkstyle/issues/13426">#13426</a> is resolved.
76       *
77       * @return a set of xdocs template file paths.
78       * @throws IOException if an I/O error occurs.
79       */
80      public static Set<Path> getXdocsTemplatesFilePaths() throws IOException {
81          final Path directory = Paths.get(DIRECTORY_PATH);
82          try (Stream<Path> stream = Files.find(directory, Integer.MAX_VALUE,
83                  (path, attr) -> {
84                      return attr.isRegularFile()
85                              && path.toString().endsWith(".xml.template");
86                  })) {
87              return stream.collect(Collectors.toUnmodifiableSet());
88          }
89      }
90  
91      /**
92       * Gets xdocs documentation file paths.
93       *
94       * @param files set of all xdoc files
95       * @return a set of xdocs config file paths.
96       */
97      public static Set<Path> getXdocsConfigFilePaths(Set<Path> files) {
98          final Set<Path> xdocs = new HashSet<>();
99          for (Path entry : files) {
100             final String fileName = entry.getFileName().toString();
101             // until https://github.com/checkstyle/checkstyle/issues/13132
102             if (fileName.startsWith("config_")
103                     || !entry.getParent().toString().matches("src[\\\\/]xdocs")
104                     && fileName.endsWith(".xml")) {
105                 xdocs.add(entry);
106             }
107         }
108         return xdocs;
109     }
110 
111     /**
112      * Gets xdocs style file paths.
113      *
114      * @param files set of all xdoc files
115      * @return a set of xdocs style file paths.
116      */
117     public static Set<Path> getXdocsStyleFilePaths(Set<Path> files) {
118         final Set<Path> xdocs = new HashSet<>();
119         for (Path entry : files) {
120             final String fileName = entry.getFileName().toString();
121             if (fileName.endsWith("_style.xml")) {
122                 xdocs.add(entry);
123             }
124         }
125         return xdocs;
126     }
127 
128     /**
129      * Gets names of checkstyle's modules which are documented in xdocs.
130      *
131      * @return a set of checkstyle's modules which have xdoc documentation.
132      * @throws ParserConfigurationException if a DocumentBuilder cannot be created which satisfies
133      *              the configuration requested.
134      * @throws IOException if any IO errors occur.
135      * @throws SAXException if any parse errors occur.
136      */
137     public static Set<String> getModulesNamesWhichHaveXdoc() throws Exception {
138         final DocumentBuilderFactory factory = DocumentBuilderFactory
139                 .newInstance();
140 
141         // Validations of XML file make parsing too slow, that is why we disable
142         // all validations.
143         factory.setNamespaceAware(false);
144         factory.setValidating(false);
145         factory.setFeature("http://xml.org/sax/features/namespaces", false);
146         factory.setFeature("http://xml.org/sax/features/validation", false);
147         factory.setFeature(
148                 "http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
149                 false);
150         factory.setFeature(
151                 "http://apache.org/xml/features/nonvalidating/load-external-dtd",
152                 false);
153 
154         final Set<String> modulesNamesWhichHaveXdoc = new HashSet<>();
155 
156         for (Path path : getXdocsConfigFilePaths(getXdocsFilePaths())) {
157             final DocumentBuilder builder = factory.newDocumentBuilder();
158             final Document document = builder.parse(path.toFile());
159 
160             // optional, but recommended
161             // FYI:
162             // http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-
163             // java-how-does-it-work
164             document.getDocumentElement().normalize();
165 
166             final NodeList nodeList = document.getElementsByTagName("section");
167 
168             for (int i = 0; i < nodeList.getLength(); i++) {
169                 final Node currentNode = nodeList.item(i);
170                 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
171                     final Element module = (Element) currentNode;
172                     final String moduleName = module.getAttribute("name");
173                     if (!"Content".equals(moduleName)
174                             && !"Overview".equals(moduleName)) {
175                         modulesNamesWhichHaveXdoc.add(moduleName);
176                     }
177                 }
178             }
179         }
180         return modulesNamesWhichHaveXdoc;
181     }
182 
183 }