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 860978a
Show file tree
Hide file tree
Showing 6 changed files with 170 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
16 changes: 16 additions & 0 deletions extractor/config/pmd/pmd-ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,31 @@
<!-- 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"/>

<!-- CognitiveComplexity is excluded as some methods, particularly those handling file processing
and configuration generation, require multiple conditional checks and error handling paths.
While we strive to keep complexity low, certain core processing methods necessarily have
higher complexity to maintain proper validation and error handling. -->
<exclude name="CognitiveComplexity"/>

<!-- CyclomaticComplexity is excluded because certain methods, especially those dealing with
file processing and configuration validation, require multiple decision points and
conditional branches. These methods need to handle various edge cases, validate inputs,
and manage different file processing scenarios, making a higher cyclomatic complexity
unavoidable for maintaining robust and reliable functionality. -->
<exclude name="CyclomaticComplexity"/>
</rule>

<rule ref="category/java/documentation.xml">
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
18 changes: 18 additions & 0 deletions extractor/src/main/resources/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
///////////////////////////////////////////////////////////////////////////////////////////////

0 comments on commit 860978a

Please sign in to comment.