Skip to content

Commit 6d9215d

Browse files
BojiZhangkopporLoayGhreebsubhramitcalixtus
authored
Implement mass addition of bib information (Closes #372) (#12025)
* feat: enhance right-click context menu with new actions (extract references, attach file, send entries, special fields) and improved layout * fix: Restore MERGE_WITH_FETCHED_ENTRY and add BATCH_MERGE_WITH_FETCHED_ENTRIES - Reintroduce previously removed MERGE_WITH_FETCHED_ENTRY action - Add new BATCH_MERGE_WITH_FETCHED_ENTRIES action after it - Fixes unintended removal of single-entry merge functionality * Refactor and optimize FetchAndMergeEntry class - Improve code structure and reduce duplication - Enhance error handling and user feedback - Streamline fetch and merge process for better performance - Implement more robust field handling and merging logic - Optimize batch processing capabilities - Refactor to use Java 8+ features for cleaner code - Improve modularity and separation of concerns - Reduce unnecessary notifications * Updated CHANGELOG.md * Added a period at the end of the description in CHANGELOG.md. * Updated JabRef_en.properties * Move and rename "Mass Getting bibliographic data" action - Relocate action from Right-click menu to "Lookup" menu for consistency - Rename action from "Mass Getting" to "Get bibliographic data from DOI/ISBN/... (fully automated)" - Other modifications not finished yet * Refactor FetchAndMergeEntry class for improved functionality and readability - Rename SUPPORTED_FIELDS to SUPPORTED_IDENTIFIER_FIELDS for clarity - Remove unnecessary hasAnySupportedField method and associated checks - Refactor duplicate code into utility methods (getFields, getJointFields) - Improve error handling with more specific exception messages - Remove edited boolean flag in favor of NamedCompound#hasEdits() - Enhance use of Optional, Stream, and method references - Reduce method parameters for better maintainability - Improve user notifications with entry citation keys - Restructure methods to separate concerns more effectively Minor modifications to MergeWithFetchedEntryAction.java to align with the new version of FetchAndMergeEntry.java * Refactor entry fetching and merging process - Introduce `MergingIdBasedFetcher` for batch operations - Create `MergeEntriesHelper` to encapsulate merge logic - Refactor `FetchAndMergeEntry` for improved separation of concerns - Implement `getFetcherForField` method in `MergingIdBasedFetcher` - Add background processing with progress updates - Enhance user notifications and error handling - Optimize logging * Updated CHANGELOG.md * refactor: Separate UI and core logic in entry merging system - Move UI-related merge handling to MultiEntryMergeWithFetchedDataAction - Relocate MergeEntriesHelper to GUI layer - Make MergingIdBasedFetcher pure business logic without UI dependencies - Add FetcherResult record to track merge changes - Enhance error handling and logging Updated JabRef_en.properties. Already Checked LocalizationConsistencyTest and MainArchitectureTest. * Refactored updateFieldIfNecessaryWithUndo method in MergeEntriesHelper class * refactor: Replace String.format with string concatenation in MergingIdBasedFetcher Switch from String.format to direct concatenation in logEntryIdentifiers method to address OpenRewrite StringFormatted warning. * refactor: Fixed incorrect placeholder in updateProgress Method * Updated JabRef_en.properties to fix the failing LocalizationConsistencyTest * refactor: optimize BibEntry merging and fetching logic: MergingIdBasedFetcher: - Replace custom logging methods with BibEntry's toString() for simpler debugging - Reorder mergeBibEntry parameters to follow source->target convention - Implement safer merge strategy that preserves existing library entry data - Remove unnecessary getMergedEntry() getter from FetcherResult record - Rename originalEntry to entryFromLibrary to align with library terminology MultiEntryMergeWithFetchedDataAction: - Convert MergeContext to a record for better immutability - Make helper methods static where possible - Simplify background task configuration using builder pattern - Consolidate Platform.runLater() calls for better UI thread management - Improve progress messaging and error handling - Move NotificationService to MergeContext for better state management MergeEntriesHelper: - Rename parameters for clarity of data flow - Simplify nested Optional chains and field update logic - Consolidate related operations into clearer methods - Improve field comparison efficiency - Optimize set operations for field management Updated Name of the Action, using Batch instead of Mass Updated JabRef_en.properties Updated CHANGELOG.md * Roll Back: FetchAndMergeEntry.java and MergeWithFetchedEntryAction.java. * Refactor: improve MergingIdBasedFetcher, BatchEntryMergeWithFetchedDataAction and MergeEntriesHelper clarity - Renamed MultiEntryMergeWithFetchedDataAction.java to BatchEntryMergeWithFetchedDataAction.java - Optimized Logger messages for clarity - Refactored the names of parameters for clarity - Refactored code by using optionals - Optimized method name for clarity Updated JabRef_en.properties * Fixed the minor import format issue. * Apply suggestions from code review Modify the CHANGELOG.md as suggested Co-authored-by: Loay Ghreeb <[email protected]> * Refactor: Reorder parameters to (fetcher, library) in merge operations - Reorder parameters to follow source->target convention in BatchEntryMergeWithFetchedDataAction and MergeEntriesHelper - Consistently use entryFromFetcher and entryFromLibrary as parameter names * Refactor: Deleted the empty line before package * Refactor: Deleted the empty line before package * refactor: Extract background processing into BatchEntryMergeTask, optimized MergingIdBasedFetcher - Moved the original single entry update feature button to the lookup menu. MergingIdBasedFetcher: - Deleted redundant getFetcherForField() method - Add StringNormalizer to standardize field value comparisons - Update existing fields when their normalized values differ - Track and report updated fields in FetcherResult - Improve debug logging for merge operations BatchEntryMergeTask (New): - Extract background processing logic from BatchEntryMergeWithFetchedDataAction - Introduce MergeContext record for operation parameters - Improve error handling and progress reporting BatchEntryMergeWithFetchedDataAction: - Split into multiple classes for better separation of concerns - Move single entry update feature button to lookup menu - Simplify to focus on operation orchestration MergeEntriesHelper: - Made methods return boolean to avoid redundant merge actions * Updated JabRef_en.properties * Apply suggestions from code review Update JabRef_en.properties as koppor suggested Co-authored-by: Oliver Kopp <[email protected]> * Modify log messages correspondingly. * Refactor: Move string normalization from StringNormalizer to StringUtil - Moved normalize() method to StringUtil - use Pattern.compile for NORMALIZE_PATTERN - Update shouldUpdateField() method in MergingIdBasedFetcher to use StringUtil.normalize() * Modified the StringUtilClassIsSmall() test in StringUtilTest class. From 774 to 804 (lineCount) * Added the cancellation feature to batch updating - Added early cancellation check in the call() method. - Switched from stream-based to for-loop processing for improved cancellation handling. - Introduced notifyCancellation() to log and notify users upon task cancellation. - Ensured processed entries are finalized and recorded with undo even when cancelled. * Attempted to fix a failed test by modifying CHANGELOG.md. * Attempted to fix a conflict by modifying CHANGELOG.md. * Attempted to fix an issue in RightClickMenu Signed-off-by: Boji Zhang <[email protected]> * Use named groups * Fix codespaces indent * Fix logger import * Remove unnecessary separator * Remove obsolete comment * Reorder lookup menu * Compilefix * add test case * Remove normalize method * Refactor RemoveWhitespace to NonSpaceWhitespaceRemover Co-authored-by: Oliver Kopp <[email protected]> * Fix CHANGELOG.md Co-authored-by: Oliver Kopp <[email protected]> * Remove argument record Co-authored-by: Oliver Kopp <[email protected]> * Simplify Co-authored-by: Oliver Kopp <[email protected]> * Rewrite Co-authored-by: Oliver Kopp <[email protected]> * l10n --------- Signed-off-by: Boji Zhang <[email protected]> Co-authored-by: KUMOAWAI <[email protected]> Co-authored-by: Oliver Kopp <[email protected]> Co-authored-by: Loay Ghreeb <[email protected]> Co-authored-by: Subhramit Basu Bhowmick <[email protected]> Co-authored-by: Carl Christian Snethlage <[email protected]>
1 parent dc145ac commit 6d9215d

File tree

18 files changed

+576
-75
lines changed

18 files changed

+576
-75
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2323
- We added a new button to toggle the file path between an absolute and relative formats in context of library properties. [#13031](https://github.com/JabRef/jabref/issues/13031)
2424
- We introduced user-configurable group 'Imported entries' for automatic import of entries from web search, PDF import and web fetchers. [#12548](https://github.com/JabRef/jabref/issues/12548)
2525
- We added automatic selection of the “Enter Identifier” tab with pre-filled clipboard content if the clipboard contains a valid identifier when opening the “Create New Entry” dialog. [#13087](https://github.com/JabRef/jabref/issues/13087)
26+
- We added batch fetching of bibliographic data for multiple entries in the "Lookup" menu. [#12275](https://github.com/JabRef/jabref/issues/12275)
2627
- We added an "Open example library" button to Welcome Tab. [#13014](https://github.com/JabRef/jabref/issues/13014)
2728
- We added automatic detection and selection of the identifier type (e.g., DOI, ISBN, arXiv) based on clipboard content when opening the "New Entry" dialog [#13111](https://github.com/JabRef/jabref/pull/13111)
2829
- We added support for import of a Refer/BibIX file format. [#13069](https://github.com/JabRef/jabref/issues/13069)

jabgui/src/main/java/org/jabref/gui/actions/StandardActions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public enum StandardActions implements Action {
3838
OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI),
3939
SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")),
4040
MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get bibliographic data from %0", "DOI/ISBN/..."), KeyBinding.MERGE_WITH_FETCHED_ENTRY),
41+
BATCH_MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get bibliographic data from %0 (fully automated)", "DOI/ISBN/...")),
4142
ATTACH_FILE(Localization.lang("Attach file"), IconTheme.JabRefIcons.ATTACH_FILE),
4243
ATTACH_FILE_FROM_URL(Localization.lang("Attach file from URL"), IconTheme.JabRefIcons.DOWNLOAD_FILE),
4344
PRIORITY(Localization.lang("Priority"), IconTheme.JabRefIcons.PRIORITY),

jabgui/src/main/java/org/jabref/gui/frame/MainMenu.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@
5353
import org.jabref.gui.linkedfile.RedownloadMissingFilesAction;
5454
import org.jabref.gui.maintable.NewLibraryFromPdfActionOffline;
5555
import org.jabref.gui.maintable.NewLibraryFromPdfActionOnline;
56+
import org.jabref.gui.mergeentries.BatchEntryMergeWithFetchedDataAction;
5657
import org.jabref.gui.mergeentries.MergeEntriesAction;
58+
import org.jabref.gui.mergeentries.MergeWithFetchedEntryAction;
5759
import org.jabref.gui.newentry.NewEntryDialogTab;
5860
import org.jabref.gui.preferences.GuiPreferences;
5961
import org.jabref.gui.preferences.ShowPreferencesAction;
@@ -284,12 +286,24 @@ private void createMenu() {
284286
}
285287

286288
lookup.getItems().addAll(
289+
// region identifier-related
287290
lookupIdentifiers,
288-
factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(dialogService, stateManager, preferences, (UiTaskExecutor) taskExecutor)),
291+
292+
factory.createMenuItem(
293+
StandardActions.MERGE_WITH_FETCHED_ENTRY,
294+
new MergeWithFetchedEntryAction(dialogService, stateManager, taskExecutor, preferences, undoManager)),
295+
296+
factory.createMenuItem(
297+
StandardActions.BATCH_MERGE_WITH_FETCHED_ENTRY,
298+
new BatchEntryMergeWithFetchedDataAction(stateManager, undoManager, preferences, dialogService, taskExecutor)),
299+
// endregion
289300

290301
new SeparatorMenuItem(),
291302

303+
// region file-related
304+
factory.createMenuItem(StandardActions.DOWNLOAD_FULL_TEXT, new DownloadFullTextAction(dialogService, stateManager, preferences, (UiTaskExecutor) taskExecutor)),
292305
factory.createMenuItem(StandardActions.FIND_UNLINKED_FILES, new FindUnlinkedFilesAction(dialogService, stateManager))
306+
// endregion
293307
);
294308

295309
final MenuItem pushToApplicationMenuItem = factory.createMenuItem(pushToApplicationCommand.getAction(), pushToApplicationCommand);
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package org.jabref.gui.mergeentries;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Optional;
6+
7+
import javax.swing.undo.UndoManager;
8+
9+
import org.jabref.gui.undo.NamedCompound;
10+
import org.jabref.gui.util.UiTaskExecutor;
11+
import org.jabref.logic.importer.fetcher.MergingIdBasedFetcher;
12+
import org.jabref.logic.l10n.Localization;
13+
import org.jabref.logic.util.BackgroundTask;
14+
import org.jabref.logic.util.NotificationService;
15+
import org.jabref.model.entry.BibEntry;
16+
17+
import org.slf4j.Logger;
18+
import org.slf4j.LoggerFactory;
19+
20+
/// A background task that handles fetching and merging of bibliography entries.
21+
/// This implementation provides improved concurrency handling, better Optional usage,
22+
/// and more robust error handling.
23+
public class BatchEntryMergeTask extends BackgroundTask<Void> {
24+
25+
private static final Logger LOGGER = LoggerFactory.getLogger(BatchEntryMergeTask.class);
26+
27+
private final NamedCompound compoundEdit;
28+
private final List<BibEntry> entries;
29+
private final MergingIdBasedFetcher fetcher;
30+
private final UndoManager undoManager;
31+
private final NotificationService notificationService;
32+
33+
private int processedEntries;
34+
private int successfulUpdates;
35+
36+
public BatchEntryMergeTask(List<BibEntry> entries,
37+
MergingIdBasedFetcher fetcher,
38+
UndoManager undoManager,
39+
NotificationService notificationService) {
40+
this.entries = entries;
41+
this.fetcher = fetcher;
42+
this.undoManager = undoManager;
43+
this.notificationService = notificationService;
44+
45+
this.compoundEdit = new NamedCompound(Localization.lang("Merge entries"));
46+
this.processedEntries = 0;
47+
this.successfulUpdates = 0;
48+
49+
setTitle(Localization.lang("Fetching and merging entry(s)"));
50+
withInitialMessage(Localization.lang("Starting merge operation..."));
51+
showToUser(true);
52+
}
53+
54+
@Override
55+
public Void call() {
56+
if (isCancelled()) {
57+
notifyCancellation();
58+
return null;
59+
}
60+
61+
List<String> updatedEntries = processMergeEntries();
62+
63+
if (isCancelled()) {
64+
notifyCancellation();
65+
updateUndoManager(updatedEntries);
66+
return null;
67+
}
68+
69+
updateUndoManager(updatedEntries);
70+
LOGGER.debug("Merge operation completed. Processed: {}, Successfully updated: {}",
71+
processedEntries, successfulUpdates);
72+
notifySuccess(successfulUpdates);
73+
return null;
74+
}
75+
76+
private void notifyCancellation() {
77+
LOGGER.debug("Merge operation was cancelled. Processed: {}, Successfully updated: {}",
78+
processedEntries, successfulUpdates);
79+
notificationService.notify(
80+
Localization.lang("Merge operation cancelled after updating %0 entry(s)", successfulUpdates));
81+
}
82+
83+
private List<String> processMergeEntries() {
84+
List<String> updatedEntries = new ArrayList<>(entries.size());
85+
86+
for (BibEntry entry : entries) {
87+
processSingleEntryWithProgress(entry).ifPresent(updatedEntries::add);
88+
if (isCancelled()) {
89+
LOGGER.debug("Cancellation requested after processing entry {}", processedEntries);
90+
break;
91+
}
92+
}
93+
94+
return updatedEntries;
95+
}
96+
97+
private Optional<String> processSingleEntryWithProgress(BibEntry entry) {
98+
updateProgress(++processedEntries, entries.size());
99+
updateMessage(Localization.lang("Processing entry %0 of %1",
100+
processedEntries,
101+
entries.size()));
102+
return processSingleEntry(entry);
103+
}
104+
105+
private Optional<String> processSingleEntry(BibEntry entry) {
106+
LOGGER.debug("Processing entry: {}", entry);
107+
return fetcher.fetchEntry(entry)
108+
.filter(MergingIdBasedFetcher.FetcherResult::hasChanges)
109+
.flatMap(result -> {
110+
boolean changesApplied = MergeEntriesHelper.mergeEntries(result.mergedEntry(), entry, compoundEdit);
111+
if (changesApplied) {
112+
successfulUpdates++;
113+
return entry.getCitationKey();
114+
}
115+
return Optional.empty();
116+
});
117+
}
118+
119+
private void updateUndoManager(List<String> updatedEntries) {
120+
if (!updatedEntries.isEmpty()) {
121+
compoundEdit.end();
122+
UiTaskExecutor.runInJavaFXThread(() -> undoManager.addEdit(compoundEdit));
123+
}
124+
}
125+
126+
private void notifySuccess(int updateCount) {
127+
String message = updateCount == 0
128+
? Localization.lang("No updates found.")
129+
: Localization.lang("Batch update successful. %0 entry(s) updated.", updateCount);
130+
notificationService.notify(message);
131+
}
132+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.jabref.gui.mergeentries;
2+
3+
import java.util.List;
4+
5+
import javax.swing.undo.UndoManager;
6+
7+
import org.jabref.gui.StateManager;
8+
import org.jabref.gui.actions.ActionHelper;
9+
import org.jabref.gui.actions.SimpleCommand;
10+
import org.jabref.gui.preferences.GuiPreferences;
11+
import org.jabref.logic.importer.fetcher.MergingIdBasedFetcher;
12+
import org.jabref.logic.l10n.Localization;
13+
import org.jabref.logic.util.NotificationService;
14+
import org.jabref.logic.util.TaskExecutor;
15+
import org.jabref.model.database.BibDatabaseContext;
16+
import org.jabref.model.entry.BibEntry;
17+
18+
/// Handles batch merging of bibliography entries with fetched data.
19+
public class BatchEntryMergeWithFetchedDataAction extends SimpleCommand {
20+
21+
private final StateManager stateManager;
22+
private final UndoManager undoManager;
23+
private final GuiPreferences preferences;
24+
private final NotificationService notificationService;
25+
private final TaskExecutor taskExecutor;
26+
27+
public BatchEntryMergeWithFetchedDataAction(StateManager stateManager,
28+
UndoManager undoManager,
29+
GuiPreferences preferences,
30+
NotificationService notificationService,
31+
TaskExecutor taskExecutor) {
32+
this.stateManager = stateManager;
33+
this.undoManager = undoManager;
34+
this.preferences = preferences;
35+
this.notificationService = notificationService;
36+
this.taskExecutor = taskExecutor;
37+
38+
this.executable.bind(ActionHelper.needsDatabase(stateManager));
39+
}
40+
41+
@Override
42+
public void execute() {
43+
if (stateManager.getActiveDatabase().isEmpty()) {
44+
return;
45+
}
46+
47+
List<BibEntry> entries = stateManager.getActiveDatabase()
48+
.map(BibDatabaseContext::getEntries)
49+
.orElse(List.of());
50+
51+
if (entries.isEmpty()) {
52+
notificationService.notify(Localization.lang("No entries available for merging"));
53+
return;
54+
}
55+
56+
MergingIdBasedFetcher fetcher = new MergingIdBasedFetcher(preferences.getImportFormatPreferences());
57+
BatchEntryMergeTask mergeTask = new BatchEntryMergeTask(
58+
entries,
59+
fetcher,
60+
undoManager,
61+
notificationService);
62+
63+
mergeTask.executeWith(taskExecutor);
64+
}
65+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.jabref.gui.mergeentries;
2+
3+
import java.util.LinkedHashSet;
4+
import java.util.Optional;
5+
import java.util.Set;
6+
7+
import org.jabref.gui.undo.NamedCompound;
8+
import org.jabref.gui.undo.UndoableChangeType;
9+
import org.jabref.gui.undo.UndoableFieldChange;
10+
import org.jabref.model.entry.BibEntry;
11+
import org.jabref.model.entry.field.Field;
12+
import org.jabref.model.entry.field.FieldFactory;
13+
import org.jabref.model.entry.types.EntryType;
14+
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
/**
19+
* Helper class for merging bibliography entries with undo support.
20+
* Source entry data is merged into the library entry, with longer field values preferred
21+
* and obsolete fields removed.
22+
*/
23+
public final class MergeEntriesHelper {
24+
25+
private static final Logger LOGGER = LoggerFactory.getLogger(MergeEntriesHelper.class);
26+
27+
private MergeEntriesHelper() {
28+
}
29+
30+
/// Merges two BibEntry objects with undo support.
31+
///
32+
/// @param entryFromFetcher The entry containing new information (source, from the fetcher)
33+
/// @param entryFromLibrary The entry to be updated (target, from the library)
34+
/// @param namedCompound Compound edit to collect undo information
35+
public static boolean mergeEntries(BibEntry entryFromFetcher, BibEntry entryFromLibrary, NamedCompound namedCompound) {
36+
LOGGER.debug("Entry from fetcher: {}", entryFromFetcher);
37+
LOGGER.debug("Entry from library: {}", entryFromLibrary);
38+
39+
boolean typeChanged = mergeEntryType(entryFromFetcher, entryFromLibrary, namedCompound);
40+
boolean fieldsChanged = mergeFields(entryFromFetcher, entryFromLibrary, namedCompound);
41+
boolean fieldsRemoved = removeFieldsNotPresentInFetcher(entryFromFetcher, entryFromLibrary, namedCompound);
42+
43+
return typeChanged || fieldsChanged || fieldsRemoved;
44+
}
45+
46+
private static boolean mergeEntryType(BibEntry entryFromFetcher, BibEntry entryFromLibrary, NamedCompound namedCompound) {
47+
EntryType fetcherType = entryFromFetcher.getType();
48+
EntryType libraryType = entryFromLibrary.getType();
49+
50+
if (!libraryType.equals(fetcherType)) {
51+
LOGGER.debug("Updating type {} -> {}", libraryType, fetcherType);
52+
entryFromLibrary.setType(fetcherType);
53+
namedCompound.addEdit(new UndoableChangeType(entryFromLibrary, libraryType, fetcherType));
54+
return true;
55+
}
56+
return false;
57+
}
58+
59+
private static boolean mergeFields(BibEntry entryFromFetcher, BibEntry entryFromLibrary, NamedCompound namedCompound) {
60+
Set<Field> allFields = new LinkedHashSet<>();
61+
allFields.addAll(entryFromFetcher.getFields());
62+
allFields.addAll(entryFromLibrary.getFields());
63+
64+
boolean anyFieldsChanged = false;
65+
66+
for (Field field : allFields) {
67+
Optional<String> fetcherValue = entryFromFetcher.getField(field);
68+
Optional<String> libraryValue = entryFromLibrary.getField(field);
69+
70+
if (fetcherValue.isPresent() && shouldUpdateField(fetcherValue.get(), libraryValue)) {
71+
LOGGER.debug("Updating field {}: {} -> {}", field, libraryValue.orElse(null), fetcherValue.get());
72+
entryFromLibrary.setField(field, fetcherValue.get());
73+
namedCompound.addEdit(new UndoableFieldChange(entryFromLibrary, field, libraryValue.orElse(null), fetcherValue.get()));
74+
anyFieldsChanged = true;
75+
}
76+
}
77+
return anyFieldsChanged;
78+
}
79+
80+
private static boolean removeFieldsNotPresentInFetcher(BibEntry entryFromFetcher, BibEntry entryFromLibrary, NamedCompound namedCompound) {
81+
Set<Field> obsoleteFields = new LinkedHashSet<>(entryFromLibrary.getFields());
82+
obsoleteFields.removeAll(entryFromFetcher.getFields());
83+
84+
boolean anyFieldsRemoved = false;
85+
86+
for (Field field : obsoleteFields) {
87+
if (FieldFactory.isInternalField(field)) {
88+
continue;
89+
}
90+
91+
Optional<String> value = entryFromLibrary.getField(field);
92+
if (value.isPresent()) {
93+
LOGGER.debug("Removing obsolete field {} with value {}", field, value.get());
94+
entryFromLibrary.clearField(field);
95+
namedCompound.addEdit(new UndoableFieldChange(entryFromLibrary, field, value.get(), null));
96+
anyFieldsRemoved = true;
97+
}
98+
}
99+
return anyFieldsRemoved;
100+
}
101+
102+
private static boolean shouldUpdateField(String fetcherValue, Optional<String> libraryValue) {
103+
// TODO: Think of a better heuristics - better "quality" is the ultimate goal (e.g., more sensible year, better page ranges, longer abstract ...)
104+
// This is difficult to get 100% right
105+
// Read more at https://github.com/JabRef/jabref/issues/12549
106+
// Currently: Only overwrite if there is nothing in the library
107+
return libraryValue.isEmpty();
108+
}
109+
}

jablib/src/main/java/org/jabref/logic/exporter/OOCalcDatabase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import org.jabref.logic.bibtex.comparator.FieldComparator;
1111
import org.jabref.logic.bibtex.comparator.FieldComparatorStack;
1212
import org.jabref.logic.layout.format.GetOpenOfficeType;
13+
import org.jabref.logic.layout.format.NonSpaceWhitespaceRemover;
1314
import org.jabref.logic.layout.format.RemoveBrackets;
14-
import org.jabref.logic.layout.format.RemoveWhitespace;
1515
import org.jabref.model.database.BibDatabase;
1616
import org.jabref.model.entry.BibEntry;
1717
import org.jabref.model.entry.field.Field;
@@ -80,7 +80,7 @@ private void addEntryRow(BibEntry entry, Element table, Document document) {
8080
addTableCell(document, row, new GetOpenOfficeType().format(entry.getType().getName()));
8181
toExportFields.forEach(field -> {
8282
if (field.equals(StandardField.TITLE)) {
83-
addTableCell(document, row, new RemoveWhitespace().format(new RemoveBrackets().format(getField(entry, StandardField.TITLE))));
83+
addTableCell(document, row, new NonSpaceWhitespaceRemover().format(new RemoveBrackets().format(getField(entry, StandardField.TITLE))));
8484
} else {
8585
addTableCell(document, row, getField(entry, field));
8686
}

jablib/src/main/java/org/jabref/logic/exporter/OpenDocumentRepresentation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import org.jabref.logic.bibtex.comparator.FieldComparator;
1111
import org.jabref.logic.bibtex.comparator.FieldComparatorStack;
1212
import org.jabref.logic.layout.format.GetOpenOfficeType;
13+
import org.jabref.logic.layout.format.NonSpaceWhitespaceRemover;
1314
import org.jabref.logic.layout.format.RemoveBrackets;
14-
import org.jabref.logic.layout.format.RemoveWhitespace;
1515
import org.jabref.model.database.BibDatabase;
1616
import org.jabref.model.entry.BibEntry;
1717
import org.jabref.model.entry.field.Field;
@@ -171,7 +171,7 @@ public Document getDOMrepresentation() {
171171
addTableCell(result, row, getField(e, StandardField.REVISION));
172172
addTableCell(result, row, getField(e, StandardField.SCHOOL));
173173
addTableCell(result, row, getField(e, StandardField.SERIES));
174-
addTableCell(result, row, new RemoveWhitespace().format(new RemoveBrackets().format(getField(e, StandardField.TITLE))));
174+
addTableCell(result, row, new NonSpaceWhitespaceRemover().format(new RemoveBrackets().format(getField(e, StandardField.TITLE))));
175175
addTableCell(result, row, getField(e, new UnknownField("reporttype")));
176176
addTableCell(result, row, getField(e, StandardField.VOLUME));
177177
addTableCell(result, row, getField(e, StandardField.YEAR));

0 commit comments

Comments
 (0)