diff --git a/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBase.java b/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBase.java index 334926c5b81..f35751658b8 100644 --- a/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBase.java +++ b/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBase.java @@ -105,7 +105,7 @@ public void guiActionSelectDocument(boolean autoSelectForSingle) throws WrappedT } catch (WrappedTargetException | IndexOutOfBoundsException | NoSuchElementException ex) { - LOGGER.warn("Problem connecting", ex); + LOGGER.warn(errorTitle, ex); OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } @@ -148,10 +148,6 @@ void showDialog(OOError err) { err.showErrorDialog(dialogService); } - void showDialog(String errorTitle, OOError err) { - err.setTitle(errorTitle).showErrorDialog(dialogService); - } - OOVoidResult collectResults(String errorTitle, List> results) { String msg = results.stream() .filter(OOVoidResult::isError) @@ -172,10 +168,6 @@ boolean testDialog(String errorTitle, OOVoidResult res) { return res.ifError(e -> showDialog(e.setTitle(errorTitle))).isError(); } - boolean testDialog(String errorTitle, List> results) { - return testDialog(errorTitle, collectResults(errorTitle, results)); - } - @SafeVarargs final boolean testDialog(String errorTitle, OOVoidResult... results) { List> resultList = Arrays.asList(results); @@ -481,7 +473,6 @@ public Optional> guiActionGetCitationEntries() { /// - Missing pageInfo means no action. /// - Missing CitationEntry means no action (no attempt to remove /// citation from the text). - /// public void guiActionApplyCitationEntries(List citationEntries) { final String errorTitle = Localization.lang("Problem modifying citation"); @@ -568,58 +559,38 @@ public void guiActionInsertEntry(List entries, OOResult fcursor = null; if (syncOptions.isPresent()) { fcursor = getFunctionalTextViewCursor(doc, errorTitle); + syncOptions.map(e -> e.setAlwaysAddCitedOnPages(openOfficePreferences.getAlwaysAddCitedOnPages())); if (testDialog(errorTitle, fcursor.asVoidResult()) || testDialog(databaseIsRequired(syncOptions.get().databases, OOError::noDataBaseIsOpenForSyncingAfterCitation))) { return; } } - syncOptions.map(e -> e.setAlwaysAddCitedOnPages(openOfficePreferences.getAlwaysAddCitedOnPages())); - try { UnoUndo.enterUndoContext(doc, "Insert citation"); if (style instanceof CitationStyle citationStyle) { // Handle insertion of CSL Style citations - try { - // Lock document controllers - disable refresh during the process (avoids document flicker during writing) - // MUST always be paired with an unlockControllers() call - doc.lockControllers(); - - if (citationType == CitationType.AUTHORYEAR_PAR) { - // "Cite" button - cslCitationOOAdapter.insertCitation(cursor.get(), citationStyle, entries, bibDatabaseContext, bibEntryTypesManager); - } else if (citationType == CitationType.AUTHORYEAR_INTEXT) { - // "Cite in-text" button - cslCitationOOAdapter.insertInTextCitation(cursor.get(), citationStyle, entries, bibDatabaseContext, bibEntryTypesManager); - } else if (citationType == CitationType.INVISIBLE_CIT) { - // "Insert empty citation" - cslCitationOOAdapter.insertEmptyCitation(cursor.get(), citationStyle, entries); - } - - // If "Automatically sync bibliography when inserting citations" is enabled - if (citationStyle.hasBibliography()) { - syncOptions.ifPresent(options -> guiActionUpdateDocument(options.databases, citationStyle)); - } - } finally { - // Release controller lock - doc.unlockControllers(); - } + insertCSLCitation(entries, + doc, + citationType, + citationStyle, + bibDatabaseContext, + bibEntryTypesManager, + cursor, + syncOptions); } else if (style instanceof JStyle jStyle) { // Handle insertion of JStyle citations - - EditInsert.insertCitationGroup(doc, - frontend.get(), - cursor.get(), - entries, - bibDatabaseContext.getDatabase(), - jStyle, + insertJStyleCitation(entries, + doc, citationType, - pageInfo); - - if (syncOptions.isPresent()) { - Update.resyncDocument(doc, jStyle, fcursor.get(), syncOptions.get()); - } + jStyle, + frontend, + cursor, + bibDatabaseContext, + syncOptions, + pageInfo, + fcursor); } } catch (NoDocumentException ex) { OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); @@ -640,6 +611,94 @@ public void guiActionInsertEntry(List entries, } } + /// Helper method for guiActionInsertEntry. Handles CSL citation insertion + /// Throws CreationException, com.sun.star.uno.Exception + /// Caught by guiActionInsertEntry + /// + /// @param entries The entries to cite. + /// @param bibDatabaseContext The database the entries belong to (all of them). Used when creating the citation mark. + /// @param citationType Indicates whether it is an in-text citation, a citation in parenthesis or an invisible citation. + /// @param citationStyle Indicates style, name and path of citation + /// @param syncOptions Indicates whether in-text citations should be refreshed in the document. Optional.empty() indicates no refresh. Otherwise, provides options for refreshing the reference list. + public void insertCSLCitation(List entries, + XTextDocument doc, + CitationType citationType, + CitationStyle citationStyle, + BibDatabaseContext bibDatabaseContext, + BibEntryTypesManager bibEntryTypesManager, + OOResult cursor, + Optional syncOptions) + throws CreationException, com.sun.star.uno.Exception { + try { + // Lock document controllers - disable refresh during the process (avoids document flicker during writing) + // MUST always be paired with an unlockControllers() call + doc.lockControllers(); + + if (citationType == CitationType.AUTHORYEAR_PAR) { + // "Cite" button + cslCitationOOAdapter.insertCitation(cursor.get(), citationStyle, entries, bibDatabaseContext, bibEntryTypesManager); + } else if (citationType == CitationType.AUTHORYEAR_INTEXT) { + // "Cite in-text" button + cslCitationOOAdapter.insertInTextCitation(cursor.get(), citationStyle, entries, bibDatabaseContext, bibEntryTypesManager); + } else if (citationType == CitationType.INVISIBLE_CIT) { + // "Insert empty citation" + cslCitationOOAdapter.insertEmptyCitation(cursor.get(), citationStyle, entries); + } + + // If "Automatically sync bibliography when inserting citations" is enabled + if (citationStyle.hasBibliography()) { + syncOptions.ifPresent(options -> guiActionUpdateDocument(options.databases, citationStyle)); + } + } finally { + // Release controller lock + doc.unlockControllers(); + } + } + + /// Helper method for guiActionInsertEntry + /// Throws PropertyVetoException, WrappedTargetException, IllegalTypeException, NotRemoveableException, CreationException, NoDocumentException + /// Exceptions caught by guiActionInsertEntry + /// + /// @param entries The entries to cite. + /// @param citationType Indicates whether it is an in-text citation, a citation in parentheses or an invisible citation. + /// @param jStyle Indicates citation formating in JStyle + /// @param bibDatabaseContext The database the entries belong to (all of them). Used when creating the citation mark. + /// @param syncOptions Indicates whether in-text citations should be refreshed in the document. Optional.empty() indicates no refresh. Otherwise, provides options for refreshing the reference list. + /// @param pageInfo A single page-info for these entries. Attributed to the last entry. + public void insertJStyleCitation(List entries, + XTextDocument doc, + CitationType citationType, + JStyle jStyle, + OOResult frontend, + OOResult cursor, + BibDatabaseContext bibDatabaseContext, + Optional syncOptions, + String pageInfo, + OOResult fcursor) + throws PropertyVetoException, + WrappedTargetException, + IllegalTypeException, + NotRemoveableException, + CreationException, + NoDocumentException { + EditInsert.insertCitationGroup(doc, + frontend.get(), + cursor.get(), + entries, + bibDatabaseContext.getDatabase(), + jStyle, + citationType, + pageInfo); + + if (syncOptions.isPresent()) { + Update.resyncDocument(doc, jStyle, fcursor.get(), syncOptions.get()); + } + } + /// GUI action "Merge citations" public void guiActionMergeCitationGroups(List databases, OOStyle style) { final String errorTitle = Localization.lang("Problem combining cite markers"); @@ -683,7 +742,7 @@ public void guiActionMergeCitationGroups(List databases, OOStyle st | PropertyVetoException | WrappedTargetException | com.sun.star.lang.IllegalArgumentException ex) { - LOGGER.warn("Problem combining cite markers", ex); + LOGGER.warn(errorTitle, ex); OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } finally { UnoUndo.leaveUndoContext(doc); @@ -708,6 +767,7 @@ public void guiActionSeparateCitations(List databases, OOStyle styl } XTextDocument doc = odoc.get(); + OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); if (testDialog(errorTitle, @@ -737,7 +797,7 @@ public void guiActionSeparateCitations(List databases, OOStyle styl | PropertyVetoException | WrappedTargetException | com.sun.star.lang.IllegalArgumentException ex) { - LOGGER.warn("Problem during separating cite markers", ex); + LOGGER.warn(errorTitle, ex); OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } finally { UnoUndo.leaveUndoContext(doc); @@ -777,7 +837,7 @@ public Optional exportCitedHelper(List databases, bool if (!result.newDatabase.hasEntries()) { dialogService.showErrorDialogAndWait( - Localization.lang("Unable to generate new library"), + Localization.lang(errorTitle), Localization.lang("Your OpenOffice/LibreOffice document references" + " no citation keys" + " which could also be found in your current library.")); @@ -787,7 +847,7 @@ public Optional exportCitedHelper(List databases, bool List unresolvedKeys = result.unresolvedKeys; if (!unresolvedKeys.isEmpty()) { dialogService.showErrorDialogAndWait( - Localization.lang("Unable to generate new library"), + Localization.lang(errorTitle), Localization.lang("Your OpenOffice/LibreOffice document references" + " at least %0 citation keys" + " which could not be found in your current library." @@ -819,20 +879,20 @@ public Optional exportCitedHelper(List databases, bool /// @param style Style. public void guiActionUpdateDocument(List databases, OOStyle style) { final String errorTitle = Localization.lang("Unable to synchronize bibliography"); - if (style instanceof JStyle jStyle) { - try { - OOResult odoc = getXTextDocument(); - if (testDialog(errorTitle, - odoc.asVoidResult(), - styleIsRequired(jStyle))) { - return; - } + OOResult odoc = getXTextDocument(); + if (testDialog(errorTitle, + odoc.asVoidResult(), + styleIsRequired(style))) { + return; + } - XTextDocument doc = odoc.get(); + XTextDocument doc = odoc.get(); - OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); + OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); + if (style instanceof JStyle jStyle) { + try { if (testDialog(errorTitle, fcursor.asVoidResult(), checkStylesExistInTheDocument(jStyle, doc), @@ -845,28 +905,7 @@ public void guiActionUpdateDocument(List databases, OOStyle style) return; } - List unresolvedKeys; - try { - UnoUndo.enterUndoContext(doc, "Refresh bibliography"); - - Update.SyncOptions syncOptions = new Update.SyncOptions(databases); - syncOptions - .setUpdateBibliography(true) - .setAlwaysAddCitedOnPages(openOfficePreferences.getAlwaysAddCitedOnPages()); - - unresolvedKeys = Update.synchronizeDocument(doc, frontend, jStyle, fcursor.get(), syncOptions); - } finally { - UnoUndo.leaveUndoContext(doc); - fcursor.get().restore(doc); - } - - if (!unresolvedKeys.isEmpty()) { - String msg = Localization.lang( - "Your OpenOffice/LibreOffice document references the citation key '%0'," - + " which could not be found in your current library.", - unresolvedKeys.getFirst()); - dialogService.showErrorDialogAndWait(errorTitle, msg); - } + updateJStyleBibliography(databases, jStyle, doc, frontend, fcursor, errorTitle); } catch (NoDocumentException ex) { OOError.from(ex).setTitle(errorTitle).showErrorDialog(dialogService); } catch (DisposedException ex) { @@ -882,64 +921,97 @@ public void guiActionUpdateDocument(List databases, OOStyle style) return; } try { - OOResult odoc = getXTextDocument(); - if (testDialog(errorTitle, odoc.asVoidResult())) { - return; - } - - XTextDocument doc = odoc.get(); - - OOResult fcursor = getFunctionalTextViewCursor(doc, errorTitle); - if (testDialog(errorTitle, fcursor.asVoidResult(), checkIfOpenOfficeIsRecordingChanges(doc))) { return; } - try { - UnoUndo.enterUndoContext(doc, "Create CSL bibliography"); - - // Collect only cited entries from all databases - List citedEntries = new ArrayList<>(); - for (BibDatabase database : databases) { - for (BibEntry entry : database.getEntries()) { - if (cslCitationOOAdapter.isCitedEntry(entry)) { - citedEntries.add(entry); - } - } - } - - // If no entries are cited, show a message and return - if (citedEntries.isEmpty()) { - dialogService.showInformationDialogAndWait( - Localization.lang("Bibliography"), - Localization.lang("No cited entries found in the document.") - ); - return; - } - - // A separate database and database context - BibDatabase bibDatabase = new BibDatabase(citedEntries); - BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(bibDatabase); - - // Lock document controllers - disable refresh during the process (avoids document flicker during writing) - // MUST always be paired with an unlockControllers() call - doc.lockControllers(); - - cslUpdateBibliography.rebuildCSLBibliography(doc, cslCitationOOAdapter, citedEntries, citationStyle, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class)); - } catch (NoDocumentException | com.sun.star.uno.Exception e) { - dialogService.notify(Localization.lang("No document found or LibreOffice insertion failure")); - LOGGER.error("Could not update CSL bibliography", e); - } finally { - doc.unlockControllers(); - UnoUndo.leaveUndoContext(doc); - fcursor.get().restore(doc); - } + updateCSLBibliography(databases, citationStyle, doc, fcursor); } catch (CreationException | com.sun.star.lang.IllegalArgumentException ex) { LOGGER.error("Could not update CSL bibliography", ex); OOError.fromMisc(ex).setTitle(errorTitle).showErrorDialog(dialogService); } } } + + /// Helper method for guiActionUpdateDocument, refreshes a JStyle bibliography. + /// + /// @param databases Must have at least one. + /// @param jStyle Indicates citation formating in JStyle. + /// @param doc Text document. + /// @param frontend,fcursor Used to synchronize document. + /// @param errorTitle Error message for user. + private void updateJStyleBibliography(List databases, JStyle jStyle, XTextDocument doc, OOFrontend frontend, + OOResult fcursor, String errorTitle) + throws CreationException, NoDocumentException, WrappedTargetException { + List unresolvedKeys; + try { + UnoUndo.enterUndoContext(doc, "Refresh bibliography"); + + Update.SyncOptions syncOptions = new Update.SyncOptions(databases); + syncOptions + .setUpdateBibliography(true) + .setAlwaysAddCitedOnPages(openOfficePreferences.getAlwaysAddCitedOnPages()); + + unresolvedKeys = Update.synchronizeDocument(doc, frontend, jStyle, fcursor.get(), syncOptions); + } finally { + UnoUndo.leaveUndoContext(doc); + fcursor.get().restore(doc); + } + + if (!unresolvedKeys.isEmpty()) { + String msg = Localization.lang( + "Your OpenOffice/LibreOffice document references the citation key '%0'," + + " which could not be found in your current library.", + unresolvedKeys.getFirst()); + dialogService.showErrorDialogAndWait(errorTitle, msg); + } + } + + /// Helper method for guiActionUpdateDocument, refreshes a CSL bibliography. + /// + /// @param databases Must have at least one. + /// @param citationStyle Citation style to update bibliography with. + /// @param doc Text document. + /// @param fcursor Used to synchronize document. + private void updateCSLBibliography(List databases, CitationStyle citationStyle, XTextDocument doc, + OOResult fcursor) + throws CreationException { + try { + UnoUndo.enterUndoContext(doc, "Create CSL bibliography"); + + // Collect only cited entries from all databases + List citedEntries = databases.stream() + .flatMap(database -> database.getEntries().stream()) + .filter(cslCitationOOAdapter::isCitedEntry) + .collect(Collectors.toCollection(ArrayList::new)); + + // If no entries are cited, show a message and return + if (citedEntries.isEmpty()) { + dialogService.showInformationDialogAndWait( + Localization.lang("Bibliography"), + Localization.lang("No cited entries found in the document.") + ); + return; + } + + // A separate database and database context + BibDatabase bibDatabase = new BibDatabase(citedEntries); + BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(bibDatabase); + + // Lock document controllers - disable refresh during the process (avoids document flicker during writing) + // MUST always be paired with an unlockControllers() call + doc.lockControllers(); + + cslUpdateBibliography.rebuildCSLBibliography(doc, cslCitationOOAdapter, citedEntries, citationStyle, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class)); + } catch (NoDocumentException | com.sun.star.uno.Exception e) { + dialogService.notify(Localization.lang("No document found or LibreOffice insertion failure")); + LOGGER.error("Could not update CSL bibliography", e); + } finally { + doc.unlockControllers(); + UnoUndo.leaveUndoContext(doc); + fcursor.get().restore(doc); + } + } } diff --git a/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java b/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java index 32b65b5de05..9fbb6290cf0 100644 --- a/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java +++ b/jabgui/src/main/java/org/jabref/gui/openoffice/OOBibBaseConnect.java @@ -218,16 +218,7 @@ public boolean isDocumentConnectionMissing() { return false; } - /// Either return a valid XTextDocument or throw NoDocumentException. - public XTextDocument getXTextDocumentOrThrow() - throws - NoDocumentException { - if (isDocumentConnectionMissing()) { - throw new NoDocumentException("Not connected to document"); - } - return this.xTextDocument; - } - + /// Returns either a valid XTextDocument in OOResult or a NoDocumentException error public OOResult getXTextDocument() { if (isDocumentConnectionMissing()) { return OOResult.error(OOError.from(new NoDocumentException())); diff --git a/jabgui/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java b/jabgui/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java index 1c78ac4dbf0..333aaedf097 100644 --- a/jabgui/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java +++ b/jabgui/src/main/java/org/jabref/gui/openoffice/OpenOfficePanel.java @@ -225,7 +225,8 @@ private void initPanel() { ooBase.guiActionSelectDocument(false); } catch (WrappedTargetException | NoSuchElementException ex) { - throw new RuntimeException(ex); + LOGGER.warn("Unable to select document to work on", ex); + OOError.fromMisc(ex).setTitle("Unable to select document to work on").showErrorDialog(dialogService); } }); @@ -348,23 +349,23 @@ private void exportEntries() { } private List getBaseList() { - List databases = new ArrayList<>(); if (openOfficePreferences.getUseAllDatabases()) { - for (BibDatabaseContext database : stateManager.getOpenDatabases()) { - databases.add(database.getDatabase()); - } - } else { - databases.add(stateManager.getActiveDatabase() - .map(BibDatabaseContext::getDatabase) - .orElse(new BibDatabase())); + return new ArrayList<>(stateManager.getOpenDatabases().stream() + .map(BibDatabaseContext::getDatabase) + .toList()); } - - return databases; + return new ArrayList<>(List.of( + stateManager.getActiveDatabase() + .map(BibDatabaseContext::getDatabase) + .orElseGet(BibDatabase::new))); } private void connectAutomatically() { DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(openOfficePreferences, dialogService); + final String errorTitle = Localization.lang("Autodetection failed"); + final String progressMessage = Localization.lang("Autodetecting paths..."); + if (officeInstallation.isExecutablePathDefined()) { connect(); } else { @@ -386,9 +387,9 @@ protected List call() { } }); - taskConnectIfInstalled.setOnFailed(_ -> dialogService.showErrorDialogAndWait(Localization.lang("Autodetection failed"), Localization.lang("Autodetection failed"), taskConnectIfInstalled.getException())); + taskConnectIfInstalled.setOnFailed(_ -> dialogService.showErrorDialogAndWait(errorTitle, errorTitle, taskConnectIfInstalled.getException())); - dialogService.showProgressDialog(Localization.lang("Autodetecting paths..."), Localization.lang("Autodetecting paths..."), taskConnectIfInstalled); + dialogService.showProgressDialog(progressMessage, progressMessage, taskConnectIfInstalled); taskExecutor.execute(taskConnectIfInstalled); } } @@ -397,6 +398,9 @@ private void connectManually() { DirectoryDialogConfiguration fileDialogConfiguration = new DirectoryDialogConfiguration.Builder().withInitialDirectory(System.getProperty("user.home")).build(); Optional selectedPath = dialogService.showDirectorySelectionDialog(fileDialogConfiguration); + final String errorTitle = Localization.lang("Could not connect to running OpenOffice/LibreOffice."); + final String extendedErrorTitle = Localization.lang("If connecting manually, please verify program and library paths."); + DetectOpenOfficeInstallation officeInstallation = new DetectOpenOfficeInstallation(openOfficePreferences, dialogService); if (selectedPath.isPresent()) { @@ -406,12 +410,12 @@ private void connectManually() { if (value) { connect(); } else { - dialogService.showErrorDialogAndWait(Localization.lang("Could not connect to running OpenOffice/LibreOffice."), Localization.lang("If connecting manually, please verify program and library paths.")); + dialogService.showErrorDialogAndWait(errorTitle, extendedErrorTitle); } }) .executeWith(taskExecutor); } else { - dialogService.showErrorDialogAndWait(Localization.lang("Could not connect to running OpenOffice/LibreOffice."), Localization.lang("If connecting manually, please verify program and library paths.")); + dialogService.showErrorDialogAndWait(errorTitle, extendedErrorTitle); } } @@ -440,6 +444,11 @@ private void updateButtonAvailability() { } private void connect() { + final String connectionError = Localization.lang("Could not connect to running OpenOffice/LibreOffice."); + final String autodetectionFailedError = Localization.lang("Autodetection failed"); + final String progressMessage = Localization.lang("Autodetecting paths..."); + final String loggerMessage = "Could not connect to running OpenOffice/LibreOffice"; + Task connectTask = new Task<>() { @Override protected OOBibBase call() throws BootstrapException, CreationException, IOException, InterruptedException { @@ -457,7 +466,9 @@ protected OOBibBase call() throws BootstrapException, CreationException, IOExcep ooBase.guiActionSelectDocument(true); } catch (WrappedTargetException | NoSuchElementException e) { - throw new RuntimeException(e); + LOGGER.warn("Unable to connect to document", e); + OOError.fromMisc(e).showErrorDialog(dialogService); + return; } // Enable actions that depend on a connection @@ -469,16 +480,16 @@ protected OOBibBase call() throws BootstrapException, CreationException, IOExcep LOGGER.error("autodetect failed", ex); switch (ex) { case UnsatisfiedLinkError unsatisfiedLinkError -> { - LOGGER.warn("Could not connect to running OpenOffice/LibreOffice", unsatisfiedLinkError); + LOGGER.warn(loggerMessage, unsatisfiedLinkError); dialogService.showErrorDialogAndWait(Localization.lang("Unable to connect. One possible reason is that JabRef " + "and OpenOffice/LibreOffice are not both running in either 32 bit mode or 64 bit mode.")); } case IOException ioException -> { - LOGGER.warn("Could not connect to running OpenOffice/LibreOffice", ioException); + LOGGER.warn(loggerMessage, ioException); - dialogService.showErrorDialogAndWait(Localization.lang("Could not connect to running OpenOffice/LibreOffice."), - Localization.lang("Could not connect to running OpenOffice/LibreOffice.") + dialogService.showErrorDialogAndWait(connectionError, + connectionError + "\n" + Localization.lang("Make sure you have installed OpenOffice/LibreOffice with Java support.") + "\n" + Localization.lang("If connecting manually, please verify program and library paths.") + "\n" + "\n" + Localization.lang("Error message:"), @@ -490,11 +501,11 @@ protected OOBibBase call() throws BootstrapException, CreationException, IOExcep } case null, default -> - dialogService.showErrorDialogAndWait(Localization.lang("Autodetection failed"), Localization.lang("Autodetection failed"), ex); + dialogService.showErrorDialogAndWait(autodetectionFailedError, autodetectionFailedError, ex); } }); - dialogService.showProgressDialog(Localization.lang("Autodetecting paths..."), Localization.lang("Autodetecting paths..."), connectTask); + dialogService.showProgressDialog(progressMessage, progressMessage, connectTask); taskExecutor.execute(connectTask); } @@ -588,15 +599,9 @@ private void pushEntries(CitationType citationType, boolean addPageInfo) { /// @return true if all entries have citation keys, if it so may be after generating them private boolean checkThatEntriesHaveKeys(List entries) { // Check if there are empty keys - boolean emptyKeys = false; - for (BibEntry entry : entries) { - if (entry.getCitationKey().isEmpty()) { - // Found one, no need to look further for now - emptyKeys = true; - break; - } - } - + // Found one, no need to look further for now + boolean emptyKeys = entries.stream() + .anyMatch(entry -> entry.getCitationKey().isEmpty()); // If no empty keys, return true if (!emptyKeys) { return true; diff --git a/jablib/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java b/jablib/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java index 60ac05eba07..0547529f3bf 100644 --- a/jablib/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java +++ b/jablib/src/main/java/org/jabref/logic/openoffice/action/EditMerge.java @@ -141,6 +141,8 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe return false; } + final String loggerMessage = "MergeCitationGroups: cursorBetween.end != currentGroupCursor.end"; + Objects.requireNonNull(state.currentGroupCursor); Objects.requireNonNull(state.cursorBetween); Objects.requireNonNull(state.prev); @@ -187,10 +189,20 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe // assume: currentGroupCursor.getEnd() == cursorBetween.getEnd() if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) { - LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end"); - throw new IllegalStateException("MergeCitationGroups failed"); + LOGGER.warn(loggerMessage); + return false; } + return checkCouldExpand(state, currentRange, loggerMessage); + } + + /// Helper method for checkAddToGroup. Tries to expand state.currentGroupCursor and state.cursorBetween by going right to reach rangeStart. + /// + /// @param state The CitationGroup to test. + /// @param currentRange The XTextRange corresponding to group. + /// @param loggerMessage The failure message for the LOGGER. + /// @return false if cannot expand, true if can. + private static boolean checkCouldExpand(ScanState state, XTextRange currentRange, String loggerMessage) { /* * Try to expand state.currentGroupCursor and state.cursorBetween by going right to reach * rangeStart. @@ -219,11 +231,10 @@ private static boolean checkAddToGroup(ScanState state, CitationGroup group, XTe // These two should move in sync: if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) { - LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end (during expand)"); - throw new IllegalStateException("MergeCitationGroups failed"); + LOGGER.warn(loggerMessage + " (during expand)"); + return false; } } - return couldExpand; } @@ -255,7 +266,7 @@ private static void addToCurrentGroup(ScanState state, CitationGroup group, XTex if (UnoTextRange.compareEnds(state.cursorBetween, state.currentGroupCursor) != 0) { LOGGER.warn("MergeCitationGroups: cursorBetween.end != currentGroupCursor.end"); - throw new IllegalStateException("MergeCitationGroups failed"); + return; } /* Store data about last entry in currentGroup */ diff --git a/jablib/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java b/jablib/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java index e5bfdb6aa10..1a90c8b7312 100644 --- a/jablib/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java +++ b/jablib/src/main/java/org/jabref/logic/openoffice/frontend/OOFrontend.java @@ -325,7 +325,7 @@ public List> viewCursorRanges(XTextDocumen /// /// Note: Here we directly communicate to the document, not through the backend. This is because mapping ranges to footnote marks does not depend on how do we mark or structure those ranges. public List> - footnoteMarkRanges(XTextDocument doc, List> citationRanges) { + footnoteMarkRanges(List> citationRanges) { // We partition by XText and use a single range from // each partition to get at the corresponding footnotemark range. @@ -402,7 +402,7 @@ static String rangeOverlapsToMessage(List>> overlaps = RangeOverlapBetween.findFirst(doc, @@ -432,7 +432,7 @@ public OOVoidResult checkRangeOverlaps(XTextDocument doc, ranges.addAll(userRanges); ranges.addAll(bibliographyRanges(doc)); ranges.addAll(citationRanges); - ranges.addAll(footnoteMarkRanges(doc, citationRanges)); + ranges.addAll(footnoteMarkRanges(citationRanges)); List>> overlaps = RangeOverlapWithin.findOverlappingRanges(doc, ranges, requireSeparation, reportAtMost); diff --git a/jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLCitationOOAdapter.java b/jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLCitationOOAdapter.java index a72125d0a8d..fb0174f9a9f 100644 --- a/jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLCitationOOAdapter.java +++ b/jablib/src/main/java/org/jabref/logic/openoffice/oocsltext/CSLCitationOOAdapter.java @@ -28,6 +28,7 @@ import com.sun.star.text.XTextCursor; import com.sun.star.text.XTextDocument; import com.sun.star.uno.Exception; +import org.jspecify.annotations.NonNull; /// This class processes CSL citations in JabRef and interacts directly with LibreOffice using an XTextDocument instance. /// It is tightly coupled with {@link CSLReferenceMarkManager} for management of reference marks tied to the CSL citations. @@ -132,7 +133,6 @@ public void insertInTextCitation(XTextCursor cursor, CitationStyle selectedStyle throws CreationException, com.sun.star.uno.Exception { setCitationStyleParameters(selectedStyle, CSLCitationType.IN_TEXT); - String style = selectedStyle.getSource(); boolean isNumericStyle = selectedStyle.isNumericStyle(); boolean isAlphanumericStyle = selectedStyle.isAlphanumericStyle(); @@ -140,26 +140,8 @@ public void insertInTextCitation(XTextCursor cursor, CitationStyle selectedStyle while (iterator.hasNext()) { BibEntry currentEntry = iterator.next(); - String inTextCitation; - - if (isAlphanumericStyle) { - inTextCitation = CSLFormatUtils.generateAlphanumericInTextCitation(currentEntry, bibDatabaseContext); - } else { - inTextCitation = CitationStyleGenerator.generateCitation(List.of(currentEntry), style, HTML_OUTPUT_FORMAT, bibDatabaseContext, bibEntryTypesManager); - } - - String formattedCitation = CSLFormatUtils.transformHTML(inTextCitation); - String finalText; - - if (isNumericStyle) { - formattedCitation = updateSingleOrMultipleCitationNumbers(formattedCitation, List.of(currentEntry)); - String prefix = CSLFormatUtils.generateAuthorPrefix(currentEntry, bibDatabaseContext); - finalText = prefix + formattedCitation; - } else if (isAlphanumericStyle) { - finalText = formattedCitation; - } else { - finalText = CSLFormatUtils.changeToInText(formattedCitation); - } + String citation = createInTextCitationText(selectedStyle, isAlphanumericStyle, isNumericStyle, currentEntry, bibDatabaseContext); + String finalText = citation; if (iterator.hasNext()) { finalText += ","; @@ -324,25 +306,9 @@ private void updateAllCitationsWithNewStyle(CitationStyle style, CSLCitationType BibEntry currentEntry = iterator.next(); // We re-generate the citation in the new style and update it in the document - String newCitation; - - if (isAlphaNumericStyle) { - newCitation = CSLFormatUtils.generateAlphanumericInTextCitation(currentEntry, unifiedBibDatabaseContext); - } else { - newCitation = CitationStyleGenerator.generateCitation(List.of(currentEntry), style.getSource(), HTML_OUTPUT_FORMAT, unifiedBibDatabaseContext, bibEntryTypesManager); - } + String citation = createInTextCitationText(style, isAlphaNumericStyle, isNumericStyle, currentEntry, unifiedBibDatabaseContext); - String formattedCitation = CSLFormatUtils.transformHTML(newCitation); - - if (isNumericStyle) { - formattedCitation = updateSingleOrMultipleCitationNumbers(formattedCitation, List.of(currentEntry)); - String prefix = CSLFormatUtils.generateAuthorPrefix(currentEntry, unifiedBibDatabaseContext); - formattedCitation = prefix + formattedCitation; - } else if (!isAlphaNumericStyle) { - formattedCitation = CSLFormatUtils.changeToInText(formattedCitation); - } - - finalText.append(formattedCitation); + finalText.append(citation); if (iterator.hasNext()) { finalText.append(","); @@ -361,20 +327,47 @@ private void updateAllCitationsWithNewStyle(CitationStyle style, CSLCitationType .toList(); // We re-generate the citation in the new style and update it in the document - String newCitation; + String citation = createCitationText(style, isAlphaNumericStyle, entries, unifiedBibDatabaseContext); - if (isAlphaNumericStyle) { - newCitation = CSLFormatUtils.generateAlphanumericCitation(entries, unifiedBibDatabaseContext); - } else { - newCitation = CitationStyleGenerator.generateCitation(entries, style.getSource(), - HTML_OUTPUT_FORMAT, unifiedBibDatabaseContext, bibEntryTypesManager); - } + markManager.updateMarkAndTextWithNewStyle(mark, citation, CSLCitationType.NORMAL); + } + } + } - String formattedCitation = CSLFormatUtils.transformHTML(newCitation); + /// Helper method for creating citations for `updateAllCitationsWithNewStyle` and `insertCitation`. + private @NonNull String createCitationText(CitationStyle style, boolean isAlphaNumericStyle, List entries, BibDatabaseContext unifiedBibDatabaseContext) { + String citation; - markManager.updateMarkAndTextWithNewStyle(mark, formattedCitation, CSLCitationType.NORMAL); - } + if (isAlphaNumericStyle) { + citation = CSLFormatUtils.generateAlphanumericCitation(entries, unifiedBibDatabaseContext); + } else { + citation = CitationStyleGenerator.generateCitation(entries, style.getSource(), HTML_OUTPUT_FORMAT, unifiedBibDatabaseContext, bibEntryTypesManager); + } + + return CSLFormatUtils.transformHTML(citation); + } + + /// Helper method for creating in-text citations for `updateAllCitationsWithNewStyle` and `insertInTextCitation`. + private @NonNull String createInTextCitationText(CitationStyle style, boolean isAlphaNumericStyle, boolean isNumericStyle, BibEntry currentEntry, BibDatabaseContext bibDatabaseContext) { + String citation; + + if (isAlphaNumericStyle) { + citation = CSLFormatUtils.generateAlphanumericInTextCitation(currentEntry, bibDatabaseContext); + } else { + citation = CitationStyleGenerator.generateCitation(List.of(currentEntry), style.getSource(), HTML_OUTPUT_FORMAT, bibDatabaseContext, bibEntryTypesManager); } + + citation = CSLFormatUtils.transformHTML(citation); + + if (isNumericStyle) { + citation = updateSingleOrMultipleCitationNumbers(citation, List.of(currentEntry)); + String prefix = CSLFormatUtils.generateAuthorPrefix(currentEntry, bibDatabaseContext); + citation = prefix + citation; + } else if (!isAlphaNumericStyle) { + citation = CSLFormatUtils.changeToInText(citation); + } + + return citation; } /// Checks if an entry has already been cited before in the document.