Skip to content

Commit

Permalink
Issue checkstyle#219: Support of config bundles with extra configurat…
Browse files Browse the repository at this point in the history
…ion files
  • Loading branch information
piyush kumar sadangi authored and piyush kumar sadangi committed Jan 15, 2025
1 parent 93fded4 commit 02d711f
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 38 deletions.
18 changes: 18 additions & 0 deletions Header/Example2/java.header
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2025 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
18 changes: 18 additions & 0 deletions Header/Example4/java.header
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2025 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
4 changes: 4 additions & 0 deletions diff-java-tool/src/main/resources/pom_template.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<sevntu-checkstyle.version>1.44.1</sevntu-checkstyle.version>
<checkstyle.config.location>https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/main/resources/google_checks.xml</checkstyle.config.location>
<checkstyle.failsOnError>true</checkstyle.failsOnError>
<config.folder>${project.basedir}/config</config.folder>
</properties>

<build>
Expand Down Expand Up @@ -62,6 +63,9 @@
<configuration>
<enableFilesSummary>false</enableFilesSummary>
<failsOnError>${checkstyle.failsOnError}</failsOnError>
<propertyExpansion>
config.folder=${config.folder}
</propertyExpansion>
</configuration>
</plugin>

Expand Down
3 changes: 3 additions & 0 deletions extractor/config/pmd/pmd-ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@
<!-- SignatureDeclareThrowsException is excluded as the project needs flexibility
in specifying exceptions for certain methods, particularly those interacting with external APIs. -->
<exclude name="SignatureDeclareThrowsException"/>

<!-- GodClass is excluded as some classes necessarily manage complex functionalities,
and attempts to refactor would decrease cohesion and increase coupling. -->
<exclude name="GodClass"/>

<!-- TooManyMethods is excluded because some classes require multiple methods
due to the breadth of their responsibilities, particularly in utility or service classes. -->
<exclude name="TooManyMethods"/>

<!-- AvoidCatchingGenericException is excluded to allow for broad exception handling
in certain scenarios where specific exceptions are not easily anticipated. -->
<exclude name="AvoidCatchingGenericException"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,47 +72,45 @@ public final class CheckstyleExampleExtractor {
private static final String PROJ_YML_PROP_FILE_PATH =
"src/main/resources/" + PROJ_YML_PROP_FILENAME;

/** The regular expression pattern for excluded file paths. */
private static final String EXCLUDED_FILE_PATTERN =
"src/xdocs-examples/resources/com/puppycrawl/tools/checkstyle/checks/regexp/"
+ "regexpmultiline/Example7.txt";

/** The regular expression pattern for example files. */
private static final String EXAMPLE_FILE_PATTERN = "Example\\d+\\.(java|txt)";

/** The subfolder name for all-in-one examples. */
private static final String ALL_IN_ONE_SUBFOLDER = "all-examples-in-one";

/**
* Number of expected arguments when processing a single input file.
*/
/** The filename for the Java header file. */
private static final String JAVA_HEADER_FILENAME = "java.header";

/** The name of the Example2 directory. */
private static final String EXAMPLE2_DIR = "Example2";

/** The name of the Example4 directory. */
private static final String EXAMPLE4_DIR = "Example4";

/** The name of the Header directory. */
private static final String HEADER_DIR = "Header";

/** Number of expected arguments when processing a single input file. */
private static final int SINGLE_INPUT_FILE_ARG_COUNT = 5;

/**
* Index of the "--input-file" flag in the argument array.
*/
/** Index of the "--input-file" flag in the argument array. */
private static final int INPUT_FILE_FLAG_INDEX = 1;

/**
* Index of the input file path in the argument array.
*/
/** Index of the input file path in the argument array. */
private static final int INPUT_FILE_PATH_INDEX = 2;

/**
* Index of the output file path in the argument array.
*/
/** Index of the output file path in the argument array. */
private static final int OUTPUT_FILE_PATH_INDEX = 3;

/**
* Index of the output file path in the argument array.
*/
/** Index of the output file path in the argument array. */
private static final int PROJECT_OUTPUT_PATH_INDEX = 4;

/**
* The buffer size for reading and writing files.
*/
/** The buffer size for reading and writing files. */
private static final int BUFFER_SIZE = 1024;

/** The constant for header path. */
private static final String JAVA_HEADER_PATH = "config/java.header";

/**
* Private constructor to prevent instantiation of this utility class.
*/
Expand Down Expand Up @@ -156,7 +154,8 @@ public static void main(final String[] args) throws Exception {
final Properties props = System.getProperties();
props.setProperty("config.folder", "${config.folder}");

final Map<String, List<Path>> moduleExamples = processExampleDirs(allExampleDirs);
final Map<String, List<Path>> moduleExamples =
processExampleDirs(allExampleDirs, checkstyleRepoPath);

YamlParserAndProjectHandler.processProjectsForExamples(PROJECT_ROOT.toString());

Expand Down Expand Up @@ -305,15 +304,16 @@ private static List<Path> findAllExampleDirs(final String checkstyleRepoPath)
* Processes example directories to map module names to their corresponding directories.
*
* @param allExampleDirs A list of paths to example directories.
* @param checkstyleRepoPath The path to the Checkstyle repository.
* @return A map associating module names with their example directories.
* @throws Exception If an unexpected error occurs.
*/
private static Map<String, List<Path>> processExampleDirs(
final List<Path> allExampleDirs)
final List<Path> allExampleDirs, final String checkstyleRepoPath)
throws Exception {
final Map<String, List<Path>> moduleExamples = new ConcurrentHashMap<>();
for (final Path dir : allExampleDirs) {
final String moduleName = processDirectory(dir.toString());
final String moduleName = processDirectory(dir.toString(), checkstyleRepoPath);
if (moduleName != null) {
moduleExamples.computeIfAbsent(moduleName, moduleKey -> new ArrayList<>()).add(dir);
}
Expand Down Expand Up @@ -365,18 +365,19 @@ private static boolean containsExampleFile(final Path path) {
* Process a directory containing example files.
*
* @param inputDir Input directory path
* @param checkstyleRepoPath The path to the Checkstyle repository.
* @return Module name if processing was successful, null otherwise
* @throws Exception If an I/O error occurs
*/
public static String processDirectory(final String inputDir) throws Exception {
public static String processDirectory(final String inputDir,
final String checkstyleRepoPath) throws Exception {
String moduleName = null;

final Path inputPath = Paths.get(inputDir);
try (Stream<Path> paths = Files.list(inputPath)) {
final List<Path> exampleFiles = paths
.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().matches(EXAMPLE_FILE_PATTERN))
.filter(path -> !path.toString().endsWith(EXCLUDED_FILE_PATTERN))
.collect(Collectors.toList());

if (!exampleFiles.isEmpty()) {
Expand All @@ -387,7 +388,7 @@ public static String processDirectory(final String inputDir) throws Exception {
Files.createDirectories(outputPath);

for (final Path exampleFile : exampleFiles) {
processExampleFile(exampleFile, outputPath);
processExampleFile(exampleFile, outputPath, checkstyleRepoPath);
}
}
}
Expand All @@ -400,36 +401,40 @@ public static String processDirectory(final String inputDir) throws Exception {
* Processes an example file and creates a corresponding subfolder in the output path.
*
* @param exampleFile The example file to process.
* @param checkstyleRepoPath The path to the Checkstyle repository.
* @param outputPath The path where the processed file's subfolder will be created.
* @throws Exception If an unexpected error occurs.
*/
private static void processExampleFile(
final Path exampleFile,
final Path outputPath)
final Path outputPath,
final String checkstyleRepoPath)
throws Exception {
final Path fileName = exampleFile.getFileName();
if (fileName != null) {
final String fileNameStr = fileName.toString().replaceFirst("\\.(java|txt)$", "");
final Path subfolderPath = outputPath.resolve(fileNameStr);
Files.createDirectories(subfolderPath);
processFile(exampleFile.toString(), subfolderPath);
processFile(exampleFile.toString(), subfolderPath, checkstyleRepoPath);
}
}

/**
* Processes an example file and generates its configuration, properties, and README.
* Also copies any known extra files if present in the same folder.
*
* @param exampleFile The path to the example file.
* @param outputPath The path where the generated content will be stored.
* @param exampleFile The path to the example file (.java or .txt).
* @param checkstyleRepoPath The path to the Checkstyle repository.
* @param outputPath The path where the generated content (config.xml, etc.) will be stored.
* @throws Exception If an unexpected error occurs.
*/
private static void processFile(
final String exampleFile,
final Path outputPath)
final Path outputPath,
final String checkstyleRepoPath)
throws Exception {
if (exampleFile != null
&& outputPath != null
&& !exampleFile.endsWith(EXCLUDED_FILE_PATTERN)) {
&& outputPath != null) {
try {
final String templateFilePath = getTemplateFilePathForExamples(exampleFile);
if (templateFilePath != null) {
Expand All @@ -438,6 +443,7 @@ private static void processFile(
writeConfigFile(outputPath, generatedContent);
copyPropertiesFile(outputPath);
generateReadme(outputPath);
handleHeaderFileIfNeeded(outputPath, checkstyleRepoPath);
}
else {
LOGGER.log(Level.WARNING,
Expand All @@ -453,6 +459,60 @@ private static void processFile(
}
}

/**
* Copies java.header from Checkstyle repository into the output folder
* (next to config.xml) if it exists.
*
* @param outputPath The folder where config.xml is placed.
* @param checkstyleRepoPath The path to Checkstyle repository
* @throws IOException if an I/O error occurs.
*/
private static void copyJavaHeaderIfNeeded(final Path outputPath,
final String checkstyleRepoPath)
throws IOException {
final Path source =
Paths.get(checkstyleRepoPath, JAVA_HEADER_PATH);

if (Files.exists(source)) {
Files.copy(source,
outputPath.resolve(JAVA_HEADER_FILENAME),
StandardCopyOption.REPLACE_EXISTING);
LOGGER.info("Copied " + JAVA_HEADER_FILENAME
+ " from " + source + " to " + outputPath);
}
else {
LOGGER.warning("No " + JAVA_HEADER_FILENAME
+ " found at " + source + ". Skipping.");
}
}

/**
* Checks if the output path requires a java.header file and copies it if needed.
*
* @param outputPath The path where config.xml is placed
* @param checkstyleRepoPath The path to Checkstyle repository
* @throws IOException if an I/O error occurs
*/
private static void handleHeaderFileIfNeeded(final Path outputPath,
final String checkstyleRepoPath)
throws IOException {
final Path parentDir = outputPath.getParent();
final String parentName = Optional.ofNullable(parentDir)
.map(Path::getFileName)
.map(Path::toString)
.orElse("");

final String folderName = Optional.ofNullable(outputPath.getFileName())
.map(Path::toString)
.orElse("");

if (HEADER_DIR.equals(parentName)
&& (EXAMPLE2_DIR.equals(folderName)
|| EXAMPLE4_DIR.equals(folderName))) {
copyJavaHeaderIfNeeded(outputPath, checkstyleRepoPath);
}
}

/**
* Writes the serialized configuration content to a config.xml.
*
Expand Down Expand Up @@ -554,7 +614,6 @@ private static List<String> getAllExampleFiles(final List<Path> exampleDirs)
paths.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().matches(EXAMPLE_FILE_PATTERN))
.map(Path::toString)
.filter(file -> !file.endsWith(EXCLUDED_FILE_PATTERN))
.forEach(allExampleFiles::add);
}
}
Expand Down Expand Up @@ -709,7 +768,6 @@ private static void generateReadmes(
try (Stream<Path> paths = Files.list(dir)) {
paths.filter(Files::isRegularFile)
.filter(path -> path.getFileName().toString().matches(EXAMPLE_FILE_PATTERN))
.filter(path -> !path.toString().endsWith(EXCLUDED_FILE_PATTERN))
.forEach(exampleFile -> {
generateIndividualReadme(exampleFile, outputPath, moduleName);
});
Expand Down

0 comments on commit 02d711f

Please sign in to comment.