package eu.svjatoslav.alyverkko_cli.commands;
-import eu.svjatoslav.alyverkko_cli.*;
+import eu.svjatoslav.alyverkko_cli.Command;
import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser;
import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.DirectoryOption;
import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.NullOption;
import static eu.svjatoslav.alyverkko_cli.Main.configuration;
import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.loadConfiguration;
+/**
+ * The JoinFilesCommand aggregates multiple files (optionally matching
+ * a specific pattern) into a single file for AI processing, typically
+ * in the mail directory.
+ *
+ * Usage Example:
+ * <pre>
+ * alyverkko-cli joinfiles -s /path/to/source -p "*.java" -t "my_topic" --edit
+ * </pre>
+ */
public class JoinFilesCommand implements Command {
+ /**
+ * A command-line parser to handle joinfiles arguments.
+ */
final Parser parser = new Parser();
+ /**
+ * Directory from which files will be joined.
+ */
public DirectoryOption sourceDirectoryOption = parser.add(new DirectoryOption("Directory to join files from"))
- .addAliases("--src-dir", "-s").mustExist();
+ .addAliases("--src-dir", "-s")
+ .mustExist();
+ /**
+ * Pattern for matching files, such as "*.java".
+ */
public StringOption patternOption = parser.add(new StringOption("Pattern to match files"))
.addAliases("--pattern", "-p");
+ /**
+ * Topic name, used as the basis for the output file name.
+ */
public StringOption topic = parser.add(new StringOption("Topic of the joined files"))
- .addAliases("--topic", "-t").setMandatory();
+ .addAliases("--topic", "-t")
+ .setMandatory();
+ /**
+ * If present, open the joined file using a text editor afterward.
+ */
public NullOption editOption = parser.add(new NullOption("Edit the joined file using text editor"))
.addAliases("--edit", "-e");
+ /**
+ * The base directory for recursion when joining files.
+ */
+ public Path sourceBaseDirectory;
+ /**
+ * The pattern used to filter files for joining, e.g. "*.java".
+ */
+ public String fileNamePattern = null;
+
+ /**
+ * The resulting output file that aggregates all matched files.
+ */
+ File outputFile;
+
+ /**
+ * @return the name of this command, i.e., "joinfiles".
+ */
@Override
public String getName() {
return "joinfiles";
}
- public Path baseDirectory;
-
- public String pattern = null;
-
- File outputFile;
-
+ /**
+ * Executes the command that joins files from a specified directory
+ * (matching an optional pattern) into one output file in the mail
+ * directory. Optionally, it can open the output file in an editor.
+ *
+ * @param cliArguments the command-line arguments after "joinfiles".
+ * @throws IOException if any IO operations fail.
+ */
@Override
public void execute(String[] cliArguments) throws IOException {
configuration = loadConfiguration();
}
if (!parser.parse(cliArguments)) {
- System.out.println("Failed to parse commandline arguments");
+ System.out.println("Failed to parse command-line arguments");
parser.showHelp();
return;
}
- // build the path to the target file that is relative to the mail directory
+ // Build the path to the target file that is relative to the mail directory
outputFile = configuration.getMailDirectory().toPath().resolve(topic.getValue() + ".org").toFile();
if (patternOption.isPresent()) {
- pattern = patternOption.getValue();
+ fileNamePattern = patternOption.getValue();
joinFiles();
}
- if (editOption.isPresent()) openFileWithEditor();
-
+ if (editOption.isPresent()) {
+ openFileWithEditor();
+ }
}
+ /**
+ * Opens the joined file with a text editor. Currently uses a
+ * command "emc" as an example—adapt as needed.
+ *
+ * @throws IOException if the launch of the editor fails.
+ */
private void openFileWithEditor() throws IOException {
- String [] cmd = {"emc", outputFile.getAbsolutePath()};
+ String[] cmd = {"emc", outputFile.getAbsolutePath()};
Runtime.getRuntime().exec(cmd);
}
+ /**
+ * Joins the matching files from the configured source directory
+ * into a single file named {@code <topic>.org} in the mail directory.
+ *
+ * @throws IOException if reading or writing files fails.
+ */
private void joinFiles() throws IOException {
-
- // Create or append to the target file
boolean appendToFile = outputFile.exists();
if (sourceDirectoryOption.isPresent()) {
- baseDirectory = sourceDirectoryOption.getValue().toPath();
+ sourceBaseDirectory = sourceDirectoryOption.getValue().toPath();
} else {
- baseDirectory = Paths.get(".");
+ sourceBaseDirectory = Paths.get(".");
}
try (BufferedWriter writer = Files.newBufferedWriter(
outputFile.toPath(), StandardCharsets.UTF_8,
appendToFile ? StandardOpenOption.APPEND : StandardOpenOption.CREATE)) {
-
- // Join files that match the pattern in the specified directory
- joinFilesRecursively(sourceDirectoryOption.getValue().toPath(), writer);
+ // Recursively join files that match the pattern
+ joinFilesRecursively(sourceBaseDirectory, writer);
}
System.out.println("Files have been joined into: " + outputFile.getAbsolutePath());
}
+ /**
+ * Recursively traverses the specified directory and writes the contents
+ * of files that match the specified {@link #fileNamePattern}.
+ *
+ * @param directoryToIndex the directory to be searched recursively.
+ * @param writer the writer to which file contents are appended.
+ * @throws IOException if file reading fails.
+ */
private void joinFilesRecursively(Path directoryToIndex, BufferedWriter writer) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryToIndex)) {
for (Path entry : stream) {
} else if (Files.isRegularFile(entry)) {
String fileName = entry.getFileName().toString();
- boolean match = GlobMatcher.match(fileName, pattern);
+ boolean match = GlobMatcher.match(fileName, fileNamePattern);
if (match) {
System.out.println("Joining file: " + fileName);
writeFile(writer, entry);
}
}
+ /**
+ * Writes the contents of a single file to the specified writer,
+ * including a small header containing the file path.
+ *
+ * @param writer the writer to which file contents are appended.
+ * @param entry the file to read and write.
+ * @throws IOException if file reading or writing fails.
+ */
private void writeFile(BufferedWriter writer, Path entry) throws IOException {
writeFileHeader(writer, entry);
- String fileContent = new String(Files.readAllBytes(entry));
+ String fileContent = new String(Files.readAllBytes(entry), StandardCharsets.UTF_8);
// remove empty lines from the beginning and end of the file
fileContent = fileContent.replaceAll("(?m)^\\s*$", "");
writer.write(fileContent + "\n");
}
+ /**
+ * Writes a small header line to indicate which file is being appended.
+ *
+ * @param writer the writer to which the header is appended.
+ * @param entry the path of the current file.
+ * @throws IOException if writing fails.
+ */
private void writeFileHeader(BufferedWriter writer, Path entry) throws IOException {
- String relativePath = baseDirectory.relativize(entry).toString();
+ String relativePath = sourceBaseDirectory.relativize(entry).toString();
writer.write("* file: " + relativePath + "\n\n");
}
}