Skip to content

Conversation

turhantolgaunal
Copy link
Contributor

@turhantolgaunal turhantolgaunal commented Sep 9, 2025

Closes #12642

The study.yml is refactored to a new format to implement SEMVER versioning, variable 'databases' is renamed to 'catalogues' for UI consistency.

In the course of working on this draft pull request the query structure is planned to be enhanced to support multiple query formats including catalog-specific variations.

Steps to test

The study.yml file created when a new slr is started should be in a new format and slr search should function as intended.
New format is:
version: 2.0
authors:

  • author
    title: title
    research-questions:
  • question
    queries:
  • query: (author="author" AND title="title")
    description: null
    lucene: null
    catalogue-specific: null
    catalogues:
  • name: ArXiv
    enabled: true
    metadata:
    notes: null
    created-date: null
    last-modified: null

Mandatory checks

@turhantolgaunal turhantolgaunal changed the title Reformatted database variable to catalogue New study.yml format Sep 9, 2025
@@ -8,7 +8,7 @@
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.ImporterPreferences;
import org.jabref.model.study.Study;
import org.jabref.model.study.StudyDatabase;
import org.jabref.model.study.StudyCatalog;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement for StudyCatalog is correct, but the patch does not address the need to use JavaFX ObservableLists for better practice in managing lists in JavaFX applications.

@@ -92,14 +92,14 @@ void studyConstructorFillsDatabasesCorrectly(@TempDir Path tempDir) {
}

private ManageStudyDefinitionViewModel getManageStudyDefinitionViewModel(Path tempDir) {
List<StudyDatabase> databases = List.of(
new StudyDatabase("ACM Portal", true));
List<StudyCatalog> catalogs = List.of(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses List.of() which is correct, but it does not utilize JavaFX ObservableLists, which is considered best practice for managing lists in JavaFX applications.

StudyYamlParser studyYAMLParser = new StudyYamlParser();
studyYAMLParser.writeStudyYamlFile(newStudy, studyRepositoryRoot.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
StudyYamlService studyYamlService = new StudyYamlService();
studyYamlService.writeStudyYamlFile(newStudy, studyRepositoryRoot.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method writeStudyYamlFile should ensure it does not return null. If it does, it should use java.util.Optional to handle potential null values.


if (CURRENT_VERSION.equals(version)) {
// Already current version, read the file normally
try (InputStream fileInputStream = Files.newInputStream(studyYamlFile)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers the entire method, which is against best practices. It should cover as few statements as possible to minimize the scope of potential exceptions.

Study study;
try {
study = new StudyYamlParser().parseStudyYamlFile(studyDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
study = new StudyYamlService().parseStudyYamlFile(studyDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers too many statements, which is against best practices. It should only cover the statement that might throw the exception to improve readability and maintainability.

private Optional<String> getVersionFromFile(Path studyYamlFile) throws IOException {
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());

try (InputStream fileInputStream = Files.newInputStream(studyYamlFile)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block covers multiple statements, which is not recommended. It should cover as few statements as possible to isolate potential exceptions.

*/
public Study parseStudyYamlFile(Path studyYamlFile) throws IOException {
if (needsMigration(studyYamlFile)) {
Study migratedStudy = StudyYamlMigrator.migrateStudyYamlFile(studyYamlFile);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method migrateStudyYamlFile should not return null. It should return an Optional or throw an exception if migration fails.

@Test
void parseStudyFileSuccessfully() throws IOException {
Study study = new StudyYamlParser().parseStudyYamlFile(testDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Study study = new StudyYamlService().parseStudyYamlFile(testDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method parseStudyYamlFile should throw a generic Exception instead of IOException to adhere to the instruction of using 'throws Exception' in the method declaration.

URL studyDefinition = StudyYamlParser.class.getResource("study-jabref-5.7.yml");
Study study = new StudyYamlParser().parseStudyYamlFile(Path.of(studyDefinition.toURI()));
URL studyDefinition = StudyYamlService.class.getResource("study-jabref-5.7.yml");
Study study = new StudyYamlService().parseStudyYamlFile(Path.of(studyDefinition.toURI()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method parseStudyYamlFile should throw a generic Exception instead of IOException to adhere to the instruction of using 'throws Exception' in the method declaration.

/**
* Creates metadata by extracting information from file and existing data
*/
private StudyMetadata createMetadata(Path studyYamlFile, V1StudyFormat oldStudy) throws IOException {
Copy link
Member

@calixtus calixtus Sep 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here: Annotate with @NonNull

Comment on lines 146 to 148
} else if (queryObj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> queryMap = (Map<String, Object>) queryObj;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (queryObj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> queryMap = (Map<String, Object>) queryObj;
} else if (queryObj instanceof Map<String, Object> queryMap) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this works with SuppressWarnings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives a unsafe casting error though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for the whole method this warning can be suppressed, as 4 lines below the warning is suppressed again. Some call it cheating, i call it an elegant way of circumventing a problem. ^^

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue is the runtime erasure of Generics, so this will be always unsafe because type info is lost at runtime. https://www.baeldung.com/java-type-erasure

@turhantolgaunal turhantolgaunal marked this pull request as draft September 27, 2025 11:02
@turhantolgaunal turhantolgaunal marked this pull request as ready for review September 29, 2025 16:31
* Generate notes for the migration
*/
private String generateMigrationNotes(V1StudyFormat oldStudy) {
StringJoiner notes = new StringJoiner(" ");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StringJoiner is used correctly here, but ensure that it is used consistently throughout the codebase for string concatenation to maintain consistency.

Copy link

trag-bot bot commented Sep 29, 2025

@trag-bot didn't find any issues in the code! ✅✨

assertEquals(expectedStudy, study);
}

@Test
void writeStudyFileSuccessfully() throws IOException {
new StudyYamlParser().writeStudyYamlFile(expectedStudy, testDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Study study = new StudyYamlParser().parseStudyYamlFile(testDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
new StudyYamlService().writeStudyYamlFile(expectedStudy, testDirectory.resolve(StudyRepository.STUDY_DEFINITION_FILE_NAME));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method writeStudyYamlFile should throw a generic Exception instead of IOException to adhere to the principle of letting JUnit handle exceptions in tests.

@@ -58,8 +58,8 @@ void readsJabRef57StudySuccessfully() throws URISyntaxException, IOException {
// If the field is "just" removed from the datamodel, one gets following exception:
// com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "last-search-date" (class org.jabref.model.study.Study), not marked as ignorable (5 known properties: "authors", "research-questions", "queries", "title", "databases"])
// This tests ensures that this exception does not occur
URL studyDefinition = StudyYamlParser.class.getResource("study-jabref-5.7.yml");
Study study = new StudyYamlParser().parseStudyYamlFile(Path.of(studyDefinition.toURI()));
URL studyDefinition = StudyYamlService.class.getResource("study-jabref-5.7.yml");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method readsJabRef57StudySuccessfully should throw a generic Exception instead of IOException and URISyntaxException to adhere to the principle of letting JUnit handle exceptions in tests.

assertEquals("Database2", catalogs.get(1).getName());

// Third database explicitly disabled
assertFalse(catalogs.get(2).isEnabled());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses assertFalse to check a boolean condition. It should assert the contents of objects using assertEquals for better clarity and precision.

implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
// TODO: Somwewhere we get a warning: unknown enum constant Id.CLASS reason: class file for com.fasterxml.jackson.annotation.JsonTypeInfo$Id not found
// implementation("com.fasterxml.jackson.core:jackson-annotations:2.19.1")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.2")
Copy link
Member

@Siedlerchr Siedlerchr Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the version in versions/build.gradle

Siedlerchr
Siedlerchr previously approved these changes Oct 14, 2025
Copy link
Member

@Siedlerchr Siedlerchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm so far

@Siedlerchr Siedlerchr requested a review from koppor October 14, 2025 17:57
@Siedlerchr Siedlerchr added the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Oct 14, 2025
@calixtus calixtus dismissed Siedlerchr’s stale review October 14, 2025 19:08

Version in versions/build.gradle is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New study.yml format

3 participants