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.meta;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.lang.reflect.Field;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.stream.Stream;
33  
34  import javax.xml.parsers.ParserConfigurationException;
35  import javax.xml.transform.TransformerException;
36  
37  import org.apache.maven.doxia.macro.MacroExecutionException;
38  
39  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
40  import com.puppycrawl.tools.checkstyle.site.JavadocScraperResultUtil;
41  import com.puppycrawl.tools.checkstyle.site.ModuleJavadocParsingUtil;
42  import com.puppycrawl.tools.checkstyle.site.PropertyDetails;
43  import com.puppycrawl.tools.checkstyle.site.SiteUtil;
44  
45  /** Class which handles all the metadata generation and writing calls. */
46  public final class MetadataGeneratorUtil {
47  
48      /** Stop instances being created. **/
49      private MetadataGeneratorUtil() {
50      }
51  
52      /**
53       * Generate metadata from the module source files available in the input argument path.
54       *
55       * @param path arguments
56       * @param moduleFolders folders to check
57       * @throws IOException ioException
58       * @throws CheckstyleException checkstyleException
59       */
60      public static void generate(String path, String... moduleFolders)
61              throws IOException, CheckstyleException {
62          final List<File> modulesToProcess =
63              getTargetFiles(path, moduleFolders);
64  
65          try {
66              for (File file : modulesToProcess) {
67                  final String fileName = file.getName();
68  
69                  if (fileName.startsWith("Abstract")
70                      && !"AbstractClassNameCheck.java".equals(fileName)) {
71                      continue;
72                  }
73  
74                  final ModuleDetails moduleDetails = getModuleDetails(file);
75                  writeMetadataFile(moduleDetails);
76              }
77          }
78          catch (MacroExecutionException macroException) {
79              throw new CheckstyleException("Failed to execute macro", macroException);
80          }
81      }
82  
83      /**
84       * Generate metadata for the given file.
85       *
86       * @param file file to generate metadata for.
87       * @return module details.
88       * @throws MacroExecutionException macroExecutionException
89       */
90      private static ModuleDetails getModuleDetails(File file) throws MacroExecutionException {
91          final String moduleName = SiteUtil.FINAL_CHECK.matcher(SiteUtil.getModuleName(file))
92              .replaceAll("");
93  
94          final Object instance = SiteUtil.getModuleInstance(moduleName);
95          final Class<?> clss = instance.getClass();
96          final String fullyQualifiedName = clss.getName();
97  
98          final String parentModule = SiteUtil.getParentModule(clss);
99          final Object parentModuleInstance = SiteUtil.getModuleInstance(parentModule);
100         final String parentModuleString = parentModuleInstance.getClass().getName();
101 
102         final ModuleType moduleType = getModuleType(moduleName);
103 
104         final Set<String> messageKeys = SiteUtil.getMessageKeys(clss);
105 
106         final String className = SiteUtil.getModuleName(file);
107         final Set<String> properties = SiteUtil.getPropertiesForDocumentation(clss, instance);
108         final Map<String, PropertyDetails> scrapedPropertyDetails = SiteUtil
109                 .buildPropertyDetails(properties, className, file.toPath(), instance);
110         String description = JavadocScraperResultUtil.getModuleDescription();
111 
112         final String notes = JavadocScraperResultUtil.getModuleNotes();
113         if (!notes.isEmpty()) {
114             description = description + "\n\n " + notes;
115         }
116 
117         final List<ModulePropertyDetails> propertiesDetails = getPropertiesDetails(
118                 scrapedPropertyDetails.values(), className, instance);
119 
120         return new ModuleDetails(moduleName, fullyQualifiedName, parentModuleString,
121             description, moduleType, propertiesDetails, new ArrayList<>(messageKeys));
122     }
123 
124     /**
125      * Get module type(check/filter/filefilter) based on module name.
126      *
127      * @param moduleName module name.
128      * @return module type.
129      */
130     private static ModuleType getModuleType(String moduleName) {
131         final ModuleType result;
132         if (moduleName.endsWith("FileFilter")) {
133             result = ModuleType.FILEFILTER;
134         }
135         else if (moduleName.endsWith("Filter")) {
136             result = ModuleType.FILTER;
137         }
138         else {
139             result = ModuleType.CHECK;
140         }
141         return result;
142     }
143 
144     /**
145      * Get property details for the given property - name, description, type, default value.
146      *
147      * @param propertiesDetails property details list.
148      * @param className the class name of the module.
149      * @param instance the instance of the module.
150      * @return property details.
151      * @throws MacroExecutionException if an error occurs.
152      */
153     private static List<ModulePropertyDetails> getPropertiesDetails(
154             Collection<PropertyDetails> propertiesDetails,
155             String className, Object instance)
156             throws MacroExecutionException {
157         final List<ModulePropertyDetails> result = new ArrayList<>(propertiesDetails.size());
158         for (PropertyDetails details : propertiesDetails) {
159             final String property = details.getName();
160             final String description = details.getDescription();
161             final Field propertyField = SiteUtil.getField(instance.getClass(), property);
162 
163             final String type = SiteUtil.getType(propertyField, property, className, instance);
164 
165             final String defaultValue = getPropertyDefaultValue(details);
166             final String validationType = getValidationType(property, propertyField);
167 
168             result.add(new ModulePropertyDetails(property, type, defaultValue,
169                     validationType, description));
170         }
171         return result;
172     }
173 
174     /**
175      * Get default value for the given property.
176      *
177      * @param details the property details.
178      * @return default value.
179      */
180     private static String getPropertyDefaultValue(PropertyDetails details) {
181         final String defaultValue;
182         if (details.getDefaultValueTokens().isEmpty()) {
183             final String raw = details.getDefaultValue();
184             if ("{}".equals(raw)) {
185                 defaultValue = "";
186             }
187             else {
188                 defaultValue = raw;
189             }
190         }
191         else {
192             defaultValue = String.join(SiteUtil.COMMA, details.getDefaultValueTokens());
193         }
194         return defaultValue;
195     }
196 
197     /**
198      * Write metadata file for the given module.
199      *
200      * @param moduleDetails module details.
201      * @throws CheckstyleException if an error occurs during writing metadata file.
202      */
203     private static void writeMetadataFile(ModuleDetails moduleDetails)
204             throws CheckstyleException {
205         try {
206             XmlMetaWriter.write(moduleDetails);
207         }
208         catch (TransformerException | ParserConfigurationException example) {
209             throw new CheckstyleException(
210                     "Failed to write metadata into XML file for module: "
211                             + moduleDetails.getName(), example);
212         }
213     }
214 
215     /**
216      * Get validation type for the given property.
217      *
218      * @param propertyName name of property.
219      * @param propertyField field of property.
220      * @return validation type.
221      */
222     private static String getValidationType(String propertyName, Field propertyField) {
223         final String validationType;
224         if (SiteUtil.TOKENS.equals(propertyName) || SiteUtil.JAVADOC_TOKENS.equals(propertyName)) {
225             validationType = "tokenSet";
226         }
227         else if (propertyField != null
228                 && ModuleJavadocParsingUtil.isPropertySpecialTokenProp(propertyField)) {
229             validationType = "tokenTypesSet";
230         }
231         else {
232             validationType = null;
233         }
234         return validationType;
235     }
236 
237     /**
238      * Get files that represent modules.
239      *
240      * @param moduleFolders folders to check
241      * @param path          rootPath
242      * @return files for scrapping javadoc and generation of metadata files
243      * @throws IOException ioException
244      */
245     private static List<File> getTargetFiles(String path, String... moduleFolders)
246             throws IOException {
247         final List<File> validFiles = new ArrayList<>();
248         for (String folder : moduleFolders) {
249             try (Stream<Path> files = Files.walk(Path.of(path + "/" + folder))) {
250                 validFiles.addAll(
251                         files.map(Path::toFile)
252                         .filter(file -> {
253                             final String fileName = file.getName();
254                             return fileName.endsWith("SuppressWarningsHolder.java")
255                                     || fileName.endsWith("Check.java")
256                                     || fileName.endsWith("Filter.java");
257                         })
258                         .toList());
259             }
260         }
261 
262         return validFiles;
263     }
264 }