Skip to content

Commit e22e812

Browse files
timothyspargclaude
andcommitted
Add file scanning infrastructure
Introduces InputOptions (shared picocli mixin for path, includes, excludes, encoding, and line-separator), FileScanner (directory scanning using plexus DirectoryScanner), FileFormatterFactory (factory for creating a FileFormatter from a base directory), and ScanConfiguration (Spring configuration wiring the factory bean). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2c3c9e3 commit e22e812

File tree

6 files changed

+429
-0
lines changed

6 files changed

+429
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.javaformat.cli;
18+
19+
import java.io.File;
20+
import java.nio.charset.Charset;
21+
import java.nio.file.FileSystems;
22+
23+
import org.jspecify.annotations.Nullable;
24+
import picocli.CommandLine.ITypeConverter;
25+
import picocli.CommandLine.Option;
26+
import picocli.CommandLine.Parameters;
27+
import picocli.CommandLine.TypeConversionException;
28+
29+
/**
30+
* Shared options for commands that scan and format files.
31+
*
32+
* @author Tim Sparg
33+
*/
34+
@PicocliManaged
35+
public class InputOptions {
36+
37+
/** Base directory to scan. */
38+
@Parameters(index = "0", arity = "0..1", defaultValue = ".", paramLabel = "PATH",
39+
description = "Base directory to scan. Default: ${DEFAULT-VALUE}", converter = DirectoryConverter.class)
40+
public File path;
41+
42+
/** Glob patterns for files to include. */
43+
@Option(names = { "-i", "--includes" }, split = ",", defaultValue = "**/*.java",
44+
description = "Repeatable or comma-separated include patterns. Default: ${DEFAULT-VALUE}",
45+
converter = GlobConverter.class)
46+
public String[] includes;
47+
48+
/** Glob patterns for files to exclude. */
49+
@Option(names = { "-x", "--excludes" }, split = ",",
50+
defaultValue = "**/target/**,**/generated-sources/**,**/generated-test-sources/**",
51+
description = "Repeatable or comma-separated exclude patterns. Default: ${DEFAULT-VALUE}",
52+
converter = GlobConverter.class)
53+
public String[] excludes;
54+
55+
/** File encoding to use when reading and writing files. */
56+
@Option(names = { "-e", "--encoding" }, defaultValue = "UTF-8",
57+
description = "File encoding. Default: ${DEFAULT-VALUE}")
58+
public Charset encoding;
59+
60+
@Option(names = { "-l", "--line-separator" },
61+
description = "Line separator (${COMPLETION-CANDIDATES}). If not specified, the existing line separator in each file is preserved.")
62+
@Nullable
63+
LineSeparator lineSeparator;
64+
65+
@Nullable
66+
public String resolveLineSeparator() {
67+
return (this.lineSeparator != null) ? this.lineSeparator.value : null;
68+
}
69+
70+
enum LineSeparator {
71+
72+
CR("\r"), LF("\n"), CRLF("\r\n");
73+
74+
private final String value;
75+
76+
LineSeparator(String value) {
77+
this.value = value;
78+
}
79+
80+
}
81+
82+
static final class GlobConverter implements ITypeConverter<String> {
83+
84+
@Override
85+
public String convert(String value) throws TypeConversionException {
86+
try {
87+
FileSystems.getDefault().getPathMatcher("glob:" + value);
88+
}
89+
catch (IllegalArgumentException ex) {
90+
throw new TypeConversionException("Invalid glob pattern '" + value + "': " + ex.getMessage());
91+
}
92+
return value;
93+
}
94+
95+
}
96+
97+
static final class DirectoryConverter implements ITypeConverter<File> {
98+
99+
@Override
100+
public File convert(String value) throws TypeConversionException {
101+
File dir = new File(value).getAbsoluteFile();
102+
if (!dir.exists()) {
103+
throw new TypeConversionException("Directory does not exist: " + dir);
104+
}
105+
if (!dir.isDirectory()) {
106+
throw new TypeConversionException("Not a directory: " + dir);
107+
}
108+
return dir;
109+
}
110+
111+
}
112+
113+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.javaformat.cli.scan;
18+
19+
import java.io.File;
20+
21+
import io.spring.javaformat.formatter.FileFormatter;
22+
23+
/**
24+
* Factory for creating a {@link FileFormatter} from a base directory.
25+
*
26+
* @author Tim Sparg
27+
*/
28+
@FunctionalInterface
29+
public interface FileFormatterFactory {
30+
31+
FileFormatter create(File basedir);
32+
33+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.javaformat.cli.scan;
18+
19+
import java.io.File;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.stream.Collectors;
23+
24+
import org.codehaus.plexus.util.DirectoryScanner;
25+
import org.springframework.stereotype.Component;
26+
27+
import io.spring.javaformat.cli.InputOptions;
28+
29+
/**
30+
* Scans files from a directory using include and exclude patterns.
31+
*
32+
* @author Tim Sparg
33+
*/
34+
@Component
35+
public class FileScanner {
36+
37+
public List<File> scan(InputOptions options) {
38+
DirectoryScanner scanner = new DirectoryScanner();
39+
scanner.setBasedir(options.path);
40+
scanner.setIncludes(options.includes);
41+
scanner.setExcludes(options.excludes);
42+
scanner.addDefaultExcludes();
43+
scanner.setCaseSensitive(false);
44+
scanner.setFollowSymlinks(false);
45+
scanner.scan();
46+
return Arrays.stream(scanner.getIncludedFiles())
47+
.map((name) -> new File(options.path, name))
48+
.collect(Collectors.toList());
49+
}
50+
51+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.javaformat.cli.scan;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
import io.spring.javaformat.config.JavaFormatConfig;
23+
import io.spring.javaformat.formatter.FileFormatter;
24+
25+
26+
27+
/**
28+
* Spring configuration for the scan sub-system.
29+
*
30+
* @author Tim Sparg
31+
*/
32+
@Configuration
33+
class ScanConfiguration {
34+
35+
@Bean
36+
FileFormatterFactory fileFormatterFactory() {
37+
return (basedir) -> new FileFormatter(JavaFormatConfig.findFrom(basedir));
38+
}
39+
40+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2017-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* File scanning and formatting infrastructure shared across commands.
19+
*
20+
* @author Tim Sparg
21+
*/
22+
@NullMarked
23+
package io.spring.javaformat.cli.scan;
24+
25+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)