Skip to content

Commit 5fa313d

Browse files
authored
Add integrity check to cli (#13848)
* extend CheckIntegrity * add check integrity to jabkit * replace writer * fix imports * fix format * add separator character handling * remove exception * adapt languages * fix language tests * fix language tests 2 * change field ranges in BibtexParser * re-add CygWinPathConverter * fix modernizer * fix modernizer2 * move fieldranges to parser * added changelog entry * change HashMap to IdentityHashMap * remove line * remove unused import * add IntegrityCheckResultWriter * address comments * add immutable Map#of * updated option handling and fix check * remove test output * add comment on constructor handling the writer and close
1 parent b5268c5 commit 5fa313d

File tree

17 files changed

+279
-17
lines changed

17 files changed

+279
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
1111

1212
### Added
1313

14+
- We added the integrity check to the jabkit cli application. [#13848](https://github.com/JabRef/jabref/issues/13848)
1415
- We added support for Cygwin-file paths on a Windows Operating System. [#13274](https://github.com/JabRef/jabref/issues/13274)
1516
- We fixed an issue where "Print preview" would throw a `NullPointerException` if no printers were available. [#13708](https://github.com/JabRef/jabref/issues/13708)
1617
- We added the option to enable the language server in the preferences. [#13697](https://github.com/JabRef/jabref/pull/13697)

docs/requirements/cli.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
parent: Requirements
3+
---
4+
# CLI
5+
6+
## Unified `--input` option across all commands
7+
`req~jabkit.cli.input-flag~1`
8+
9+
All `jabkit` commands that need a file input must have the `--input` option to specify the input file.
10+
See [ADR 45](../decisions/0045-use-input-flag-always-for-input-files.md) for more details.
11+
12+
Needs: impl
13+
14+
<!-- markdownlint-disable-file MD022 -->

jabkit/src/main/java/org/jabref/cli/ArgumentProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
// sorted alphabetically
4242
subcommands = {
4343
CheckConsistency.class,
44-
// CheckIntegrity.class,
44+
CheckIntegrity.class,
4545
Convert.class,
4646
Fetch.class,
4747
GenerateBibFromAux.class,

jabkit/src/main/java/org/jabref/cli/CheckConsistency.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class CheckConsistency implements Callable<Integer> {
3333
@Mixin
3434
private ArgumentProcessor.SharedOptions sharedOptions = new ArgumentProcessor.SharedOptions();
3535

36+
// [impl->req~jabkit.cli.input-flag~1]
3637
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
3738
private Path inputFile;
3839

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,117 @@
11
package org.jabref.cli;
22

3+
import java.io.IOException;
4+
import java.io.OutputStreamWriter;
5+
import java.io.Writer;
36
import java.nio.file.Path;
7+
import java.util.List;
8+
import java.util.Locale;
9+
import java.util.Optional;
10+
import java.util.concurrent.Callable;
11+
import java.util.stream.Collectors;
412

513
import org.jabref.cli.converter.CygWinPathConverter;
14+
import org.jabref.logic.importer.ParserResult;
15+
import org.jabref.logic.integrity.IntegrityCheck;
16+
import org.jabref.logic.integrity.IntegrityCheckResultCsvWriter;
17+
import org.jabref.logic.integrity.IntegrityCheckResultErrorFormatWriter;
18+
import org.jabref.logic.integrity.IntegrityCheckResultTxtWriter;
19+
import org.jabref.logic.integrity.IntegrityCheckResultWriter;
20+
import org.jabref.logic.integrity.IntegrityMessage;
21+
import org.jabref.logic.journals.JournalAbbreviationLoader;
622
import org.jabref.logic.l10n.Localization;
23+
import org.jabref.model.database.BibDatabaseContext;
24+
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
import picocli.CommandLine;
728

829
import static picocli.CommandLine.Command;
930
import static picocli.CommandLine.Mixin;
1031
import static picocli.CommandLine.Option;
11-
import static picocli.CommandLine.Parameters;
1232

1333
@Command(name = "check-integrity", description = "Check integrity of the database.")
14-
class CheckIntegrity implements Runnable {
34+
class CheckIntegrity implements Callable<Integer> {
35+
36+
private static final Logger LOGGER = LoggerFactory.getLogger(CheckIntegrity.class);
37+
38+
@CommandLine.ParentCommand
39+
private ArgumentProcessor argumentProcessor;
1540

1641
@Mixin
1742
private ArgumentProcessor.SharedOptions sharedOptions = new ArgumentProcessor.SharedOptions();
1843

19-
@Parameters(index = "0", converter = CygWinPathConverter.class, description = "BibTeX file to check", arity = "0..1")
44+
// [impl->req~jabkit.cli.input-flag~1]
45+
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
2046
private Path inputFile;
2147

22-
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file")
23-
private Path inputOption;
48+
@Option(names = {"--output-format"}, description = "Output format: errorformat, txt or csv", defaultValue = "errorformat")
49+
private String outputFormat;
2450

25-
@Option(names = {"--output-format"}, description = "Output format: txt or csv")
26-
private String outputFormat = "txt"; // FixMe: Default value?
51+
// in BibTeX it could be preferences.getEntryEditorPreferences().shouldAllowIntegerEditionBibtex()
52+
@Option(names = {"--allow-integer-edition"}, description = "Allows Integer edition", negatable = true, defaultValue = "true", fallbackValue = "true")
53+
private boolean allowIntegerEdition;
2754

2855
@Override
29-
public void run() {
56+
public Integer call() {
57+
Optional<ParserResult> parserResult = ArgumentProcessor.importFile(
58+
inputFile,
59+
"bibtex",
60+
argumentProcessor.cliPreferences,
61+
sharedOptions.porcelain);
62+
if (parserResult.isEmpty()) {
63+
System.out.println(Localization.lang("Unable to open file '%0'.", inputFile));
64+
return 2;
65+
}
66+
67+
if (parserResult.get().isInvalid()) {
68+
System.out.println(Localization.lang("Input file '%0' is invalid and could not be parsed.", inputFile));
69+
return 2;
70+
}
71+
3072
if (!sharedOptions.porcelain) {
3173
System.out.println(Localization.lang("Checking integrity of '%0'.", inputFile));
3274
System.out.flush();
3375
}
3476

35-
// TODO: Implement integrity checking
77+
BibDatabaseContext databaseContext = parserResult.get().getDatabaseContext();
78+
79+
IntegrityCheck integrityCheck = new IntegrityCheck(
80+
databaseContext,
81+
argumentProcessor.cliPreferences.getFilePreferences(),
82+
argumentProcessor.cliPreferences.getCitationKeyPatternPreferences(),
83+
JournalAbbreviationLoader.loadRepository(argumentProcessor.cliPreferences.getJournalAbbreviationPreferences()),
84+
allowIntegerEdition
85+
);
86+
87+
List<IntegrityMessage> messages = databaseContext.getEntries().stream()
88+
.flatMap(entry -> integrityCheck.checkEntry(entry).stream())
89+
.collect(Collectors.toList());
90+
91+
messages.addAll(integrityCheck.checkDatabase(databaseContext.getDatabase()));
92+
93+
Writer writer = new OutputStreamWriter(System.out);
94+
IntegrityCheckResultWriter checkResultWriter;
95+
switch (outputFormat.toLowerCase(Locale.ROOT)) {
96+
case "errorformat" ->
97+
checkResultWriter = new IntegrityCheckResultErrorFormatWriter(writer, messages, parserResult.get(), inputFile);
98+
case "txt" ->
99+
checkResultWriter = new IntegrityCheckResultTxtWriter(writer, messages);
100+
case "csv" ->
101+
checkResultWriter = new IntegrityCheckResultCsvWriter(writer, messages);
102+
default -> {
103+
System.out.println(Localization.lang("Unknown output format '%0'.", outputFormat));
104+
return 3;
105+
}
106+
}
107+
108+
try {
109+
checkResultWriter.writeFindings();
110+
writer.flush();
111+
} catch (IOException e) {
112+
LOGGER.error("Error writing results", e);
113+
return 2;
114+
}
115+
return 0;
36116
}
37117
}

jabkit/src/main/java/org/jabref/cli/Convert.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class Convert implements Runnable {
3737
@Mixin
3838
private ArgumentProcessor.SharedOptions sharedOptions = new ArgumentProcessor.SharedOptions();
3939

40+
// [impl->req~jabkit.cli.input-flag~1]
4041
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input file", required = true)
4142
private Path inputFile;
4243

jabkit/src/main/java/org/jabref/cli/GenerateBibFromAux.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66
import java.util.stream.Collectors;
77

8+
import org.jabref.cli.converter.CygWinPathConverter;
89
import org.jabref.logic.auxparser.AuxParser;
910
import org.jabref.logic.auxparser.AuxParserResult;
1011
import org.jabref.logic.auxparser.AuxParserStatisticsProvider;
@@ -35,8 +36,9 @@ class GenerateBibFromAux implements Runnable {
3536
@Option(names = "--aux", required = true)
3637
private Path auxFile;
3738

38-
@Option(names = "--input", required = true)
39-
private String inputFile;
39+
// [impl->req~jabkit.cli.input-flag~1]
40+
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
41+
private Path inputFile;
4042

4143
@Option(names = "--output")
4244
private Path outputFile;

jabkit/src/main/java/org/jabref/cli/GenerateCitationKeys.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Optional;
55
import java.util.stream.Collectors;
66

7+
import org.jabref.cli.converter.CygWinPathConverter;
78
import org.jabref.logic.citationkeypattern.CitationKeyGenerator;
89
import org.jabref.logic.importer.ParserResult;
910
import org.jabref.logic.l10n.Localization;
@@ -28,8 +29,9 @@ public class GenerateCitationKeys implements Runnable {
2829
@Mixin
2930
private ArgumentProcessor.SharedOptions sharedOptions = new ArgumentProcessor.SharedOptions();
3031

31-
@Option(names = "--input", description = "The input .bib file.", required = true)
32-
private String inputFile;
32+
// [impl->req~jabkit.cli.input-flag~1]
33+
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
34+
private Path inputFile;
3335

3436
@Option(names = "--output", description = "The output .bib file.")
3537
private Path outputFile;

jabkit/src/main/java/org/jabref/cli/PdfUpdate.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import javax.xml.parsers.ParserConfigurationException;
1010
import javax.xml.transform.TransformerException;
1111

12+
import org.jabref.cli.converter.CygWinPathConverter;
1213
import org.jabref.logic.FilePreferences;
1314
import org.jabref.logic.bibtex.FieldPreferences;
1415
import org.jabref.logic.exporter.EmbeddedBibFilePdfExporter;
@@ -49,8 +50,9 @@ class PdfUpdate implements Runnable {
4950
@Option(names = {"-k", "--citation-key"}, description = "Citation keys", required = true)
5051
private List<String> citationKeys = List.of(); // ToDo: check dedault value
5152

52-
@Option(names = "--input", description = "Input file", required = true)
53-
private Path inputFile; // Local files only
53+
// [impl->req~jabkit.cli.input-flag~1]
54+
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
55+
private Path inputFile;
5456

5557
@Option(names = "--input-format", description = "Input format of the file", required = true)
5658
private String inputFormat = "*";

jabkit/src/main/java/org/jabref/cli/Search.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Search implements Runnable {
4747
@Option(names = {"--query"}, description = "Search query", required = true)
4848
private String query;
4949

50+
// [impl->req~jabkit.cli.input-flag~1]
5051
@Option(names = {"--input"}, converter = CygWinPathConverter.class, description = "Input BibTeX file", required = true)
5152
private Path inputFile;
5253

0 commit comments

Comments
 (0)